Developer guide
Tenant isolation, authorization, and security
Enforce the complete server-side boundary for every read, mutation, export, download, job, webhook, and integration call.
MaintainerIntegratorReviewer
Responsibility
A permission answers whether an actor may perform an action in principle. Institute membership, record ownership, relationship, subscription, and domain state determine whether that actor may perform it on this record now.
Components and data flow
- Authentication middleware establishes the actor and session.
- Institute context and membership constrain tenant-owned queries.
- Permission middleware or policies authorize the requested action.
- Ownership, relationship, and record-state checks protect the individual resource.
- Remote URL guards, signature validation, sanitization, and file validation protect external boundaries.
Rules that must remain true
- Never trust a client-supplied institute ID as authorization.
- Scope before retrieval so an unauthorized record is not loaded and filtered later.
- Private files use authorized routes and safe response headers.
- External URLs deny private, loopback, reserved, redirect-to-private, and unsafe-scheme targets.
- Sensitive values are masked and never returned after storage.
Failure modes
- An unscoped
find()creates cross-tenant exposure. - A hidden button without route authorization is bypassable.
- Webhook retries without replay protection duplicate state changes.
- Untrusted HTML, filenames, or URLs cross into a privileged context.
Safe extension guidance
- Add explicit policy or middleware coverage for every new sensitive route.
- Use validated DTO-like arrays from form requests rather than raw request input.
- Record security-relevant decisions with redacted context and stable correlation IDs.
- Design retention and deletion for every new category of private data.
Required tests
- Cross-institute reads and mutations fail for every affected endpoint.
- Direct URLs and downloads enforce the same access as navigation.
- Stored content and filenames cannot execute active content.
- Remote URL, webhook signature, replay, and redirect protections reject adversarial inputs.
- Logs and JSON errors contain no secrets or stack traces.
Release gate: A feature that lacks tenant, permission, ownership/state, and malicious-input tests is not ready for release.