Security review packet
Control detail, including the controls we do not have.
Written for a questionnaire rather than for a first impression. Each control below states its mechanism, its boundary, and whether it is live today, written down but not built, or not held at all.
How to read this page
Every control carries one of three standings
The same three the product uses inside a workspace. A trust page that graded itself on a scale of its own invention would be describing a different product from the one a customer signed for.
- LiveImplemented and running in production today.
- ProposedWritten down as a requirement. Not built. Do not plan around it.
- Not heldWe do not have this. Stated here rather than discovered in a questionnaire.
Isolation mechanism
Tenant isolation is enforced by the database
Live
Application authorization still checks the signed-in person’s organization and role. Row Level Security provides the independent data-layer boundary if an application query is incomplete or incorrect.
Every application table is protected by Postgres Row Level Security (RLS), and every one of them uses forced RLS. FORCE also subjects table owners to policies unless they are superusers or hold BYPASSRLS. Those privileged operator paths can bypass row security; application security-definer functions enforce explicit session and scope checks. A schema trigger applies the same setting to new application tables.
No policy in the schema carries an unconditional predicate, and the unauthenticated role holds no privilege in the application schema at all — a signed-out request reaches nothing.
A cross-tenant reference is a constraint violation, not a bug
Tenant isolation covers references as well as reads. Relationships such as requirement-to-project are declared on the pair (organization, record), so a row in one organization cannot point at a record in another: the reference does not satisfy the constraint and cannot be written. Further constraints hold the rules that are not about tenancy — that an acceptance names a real person, that the person who publishes a revision is not the person who submitted it, that a monitoring check can never be recorded as if it had blocked something.
Two ways a boundary like this is bypassed
Views and privileged functions receive explicit treatment because both can change which role evaluates a policy. Every view in the schema runs with the caller’s security context, so the caller’s tenant policies remain in force. Every function that runs as its owner pins the namespace used to resolve referenced objects, which is what stops a mutable search path from becoming a privilege-escalation primitive.
Cross-tenant refusal is release-tested
Each release uses a signed-in identity from one organization to attempt reads and writes against other organizations across every tenant-scoped table. The proof uses the same interface as the browser, and every cross-tenant attempt must be refused. The run enumerates the tables from the catalog rather than from a list, so a table added tomorrow is covered tomorrow; a table added with no organization on it is reported by name instead of being skipped quietly.
Historical isolation proof · LockedIn Labs FDE External hosted service. Recorded 26 August 2026, 13 days ago: the run recorded no readable foreign row or accepted foreign write. The retained schema measurement describes LockedIn Labs FDE External hosted service, recorded on 26 August 2026, 13 days ago. This historical account is built from that measurement rather than from a live query. No LockedIn Labs FDE production verification is established by these receipts. Workspace security settings show current session and schema reads separately.
- scripts/prove-tenancy.mjs
- scripts/measure-posture.mjs
- src/app/app/[org]/settings/security/page.tsx
Encryption and location
Encryption and data location
Live
Core application data is stored by Supabase and the application is delivered by Netlify. Encryption at rest and in transit is provided by those managed platforms.
When the optional voice assistant is enabled, a tenant-scoped workspace brief and the live conversation are sent to Google Gemini, as listed under subprocessors.
- At restThe database and its backups are stored encrypted with AES-256 by the managed Postgres platform.
- In transitEvery connection — browser to application, application to database — is TLS 1.2 or above, terminated by the application host and by the database platform. There is no unencrypted listener to reach.
- Where it sitsCustomer data is held in the United States and nowhere else. The database runs in AWS
us-east-1(Northern Virginia); the application’s server-side rendering and its API routes run in AWSus-east-2(Ohio). Static pages that carry no customer data are cached on the host’s global network, which is why a page can load quickly from outside the United States without any register content leaving it.
Current scope: customer-managed encryption keys and field-level or column-level encryption are not currently offered. The regions above are the ones in use today, and they are stated as fact rather than as a commitment: there is no contractual residency term, no EU or UK region, and no undertaking not to move a region on notice. A tenancy that needs residency in writing needs a contract clause this page cannot substitute for.
Identity and sessions
How people sign in, and what a role can reach
Live
Identity is handled by Supabase Auth. This application never receives, stores or logs a password or a verification code: the credential is exchanged with the auth service, and what comes back is a session.
Two methods are available on this deployment today — email and password, or a single-use link sent to a verified address — and either may be followed by a second factor.
- Two-step verification, for local accountsA time-based code from an authenticator app. Any member may enroll, and once a factor is enrolled it cannot be skipped — every request into a workspace is checked, and a session that has not presented the factor is sent back to do so. SMS and hardware security keys are not offered; the factor is an authenticator app or nothing. Enrollment is not currently demanded of anybody on this deployment. The rule that requires it of the two administrative grades, and the setting that lets a workspace require it of everyone, are built and switched off; they are turned on per deployment rather than on the day the code merged, because demanding a factor stops people who have not been told it is coming. Proposed
- Recovery that is not a back doorEnrollment issues ten single-use recovery codes, shown once and stored only as digests. They are not emailed. Nobody at LockedIn Labs FDE can issue, read or replace another person’s codes, and no support process can remove somebody’s second factor on request — every route to those codes takes the caller’s own identity and no account argument, so an administrator with full database access can destroy a set and still cannot become its owner. Spending a code removes the authenticator, retires the remaining nine and signs out every session on the account; it does not grant access, and the password is still required afterwards.
- Session lifetimeEight hours idle, twelve hours absolute, enforced by this application on every request. Reaching either limit revokes the session at the auth service rather than only clearing the browser. The figures are the application’s; the auth service’s own inactivity timeout and session timebox are not set on this project, so a session that never reaches this application is not subject to them.
- Re-authentication before privileged actsChanging what a member can do, removing access, transferring ownership, minting a synchronization token, changing sign-in policy or touching your own second factor require a proof of identity from the last fifteen minutes. Freshness is read from the auth service’s own signed statement of when a credential was last presented, not from anything this application asserts about itself. The mechanism is live and is currently wired to the account-security acts; the membership and token surfaces are named above as the intended scope and are not behind it yet. Proposed
- SessionsHeld in a cookie marked
SecureandSameSite=Lax, so it is never sent over plain HTTP and never rides a cross-site request. Access tokens are short-lived and revalidated against the auth server on each request rather than trusted from the cookie. Signing out revokes the session at the auth service instead of only clearing the browser, and it answers to POST alone — a sign-out reachable by GET is a link anyone can make you click. - Five roles, checked in the databaseAdministering the tenant and holding authority over delivery evidence are deliberately separate grades: the grade that manages membership does not thereby acquire the power to publish evidence, change billing, transfer ownership or delete the tenancy.
- Least privilege is the default state, not a settingA new member reaches nothing until a membership row places them in one organization at one grade. There is no cross-organization role and no global search: a person with no membership in a workspace gets the same answer from the database as a stranger.
Not held today, and stated plainly because a reviewer will ask. SAML single sign-on is built, and it is activated for no tenant on this deployment — the auth service’s SAML support is switched off at the project level, and no identity-provider connection exists. A workspace can record which verified domain it intends to federate and whether a password should still be accepted for it, but nothing federates today, and the setting that refuses the password route for a federated domain is recorded rather than enforced. Two-step verification for local accounts is built and available to enroll, and is not currently demanded of anybody, so a single credential is what is actually carrying this deployment. Proposed
Where the second factor is checked, precisely. On this application’s own request path, on every request into a workspace, for an account that has enrolled one. It is not checked by the database: the assurance predicate exists and is tested, and it is deliberately not yet applied as a restrictive policy across the register, because the failure mode of getting that wrong is every member of every tenancy locked out at once. The practical consequence is exact and worth stating — a request made directly against the auth service’s data API with a valid single-factor token does not pass through the gate described here. Closing that is a reviewed change of its own. Proposed
Password policy, since it is usually asked about in the same breath. Twelve characters minimum, upper case, lower case and a digit required, and every password is checked against the Have I Been Pwned breach corpus and refused if it appears there. There is no rotation requirement, deliberately: forced rotation is no longer recommended practice and produces worse passwords, not better ones.
The session cookie is deliberately not HttpOnly. The Supabase browser client has to read the session to attach it to its own requests, so the flag cannot simply be set — moving the session behind an HttpOnly cookie is a server-only authentication flow, not a one-line change. Two things carry the weight instead: every workspace and sign-in route is served under a nonce-based script policy that refuses injected inline script, and the tenant boundary lives in the database rather than in the token, so a stolen session still reaches only what that person’s own membership allows.
Agent authority
What an agent may call, and what happens when it may not
Live
The catalog of tools an agent can call is derived from the server that publishes them, and anything that writes is denied until a named person grants it.
The catalog of callable tools is derived from the server that publishes them rather than typed into the application, so the register cannot describe a door that does not exist or miss one that does. Each tool declares whether it reads or whether it writes, and that single distinction decides the default: reads are available, and a tool that writes is denied until somebody holding model-approval authority grants it in their own words. A grant is revocable, and revocation is whole rather than partial — a half-revoked permission is a permission.
The check runs before the door, not beside it
The workspace endpoint asks the permission register one question — may this credential call this tool — and either returns, meaning the door runs, or throws, meaning it never executes. A check that runs in parallel with the work it authorizes has authorized nothing, so the order is the mechanism rather than an implementation detail. The answer comes from the same function the workspace console reads, which is what stops the screen describing a posture the door does not apply.
Advisory enforcement is reported as what it is. When a workspace has deliberately set a decision to advisory, the door runs and the attempt is written to the refusal register marked as having got through — because a control a workspace chose to watch rather than to apply is not a control it is protected by, and collapsing the two would quietly make every advisory decision look blocking.
A refusal is a record, and it is screened
A refusal names the tool, the reason and whether the call was stopped or only watched. It never names the payload. The free text on a grant’s basis and on a refusal’s detail is checked at write against the patterns that identify a person — a social security number, a medical record number, a labeled date of birth or member identifier — and a write carrying one is refused with the pattern named rather than stored and reported afterwards.
What a run is held to, and what came back
9 operating policies ship with the product as catalog content, and a workspace adopts each one at an enforcement level it chooses; 7 of them ship as blocking and the rest report without stopping the work. Adoption is an act by a named person holding the same authority that approves a model, and retiring one leaves both records standing, so a run stays explainable under the rules of its own day.
A verdict for a run is sealed with a digest over the bytes it was about. There is no update policy on the verdict table, so a sealed verdict cannot be edited by anyone holding a token; a corrected verdict is a new run, which is the honest shape. A policy with no verdict on a run reads as not evaluated — never as passed. Verdicts are append-only, and a waiver that names nobody is refused by the database.
- IL-01An agent may not grant itself authorityShips blocking
- IL-02A result is a proposal until a person accepts itShips blocking
- IL-03No opaque executionShips blocking
- IL-04Regulated content stays inside its approved boundaryShips blocking
- IL-05A gate seals before a deployShips blocking
- IL-06Every run names the model it ran underShips advisory
- IL-07A waiver names a personShips blocking
- IL-08A run narrates what it didShips advisory
- IL-09Secrets are located, never storedShips blocking
Where this stops. Adoption records the rule; the runtime applies it. Nothing on the policy register can stop a run by itself, and the register says so rather than letting a green screen imply it. A permission is a decision recorded in advance and a refusal is the record of an attempt — both are properties of the register rather than evidence that a particular workspace has exercised them, which is why a policy with no verdict is reported as not evaluated instead of being folded into a pass.
- supabase/migrations/20260820190000_tool_permission.sql
- src/app/api/mcp/route.ts
- src/lib/tool-permission/gate.ts
- supabase/migrations/20260820020000_agentic_harness.sql
The AI control plane
Which model saw the work, where it ran, and what it was allowed to see
Live
Models are approved one at a time and recorded with where they run, and each approval states in writing whether that model may process regulated content.
- On premisesRuns on hardware this organization controls. Prompts do not leave the network.
- Private cloudA tenant-isolated deployment under contract. Handling is governed by that agreement rather than public terms.
- Vendor hostedA public API. Work sent here leaves the organization's boundary.

The posture is the permission
Every approval carries a hosting posture — the organization's own hardware, a tenant-isolated private cloud under contract, or a vendor's public API — and the posture is what decides what the model may be shown. A workspace holds several credentials at once, each with its own boundary and its own ceiling, and a retired one stays listed with the date and the person, because the runs that happened under it did happen.
Approving a model, retiring one and choosing which credential is in force are all acts by a named person. The custody trigger takes the actor from the authenticated session rather than from anything the client sends, and a session with no identity behind it is refused outright with the reason stated — so a workspace cannot end up holding an approval that nobody made.
Credentials are located, never held
The register records a credential’s label, its hosting posture, its ceiling, what it has spent this month and when it was last used. The handle to the secret in the vault and the key’s fingerprint are not selectable by any workspace role, including the grades that administer the tenancy; the runtime reaches the secret through a privileged function that returns it to the server and to nobody else, and the standing report every member reads carries neither the pointer nor the fingerprint.
Current scope. The register records the posture a workspace stated for each approval and the address it gave, and it holds the decision to the person who made it. It does not independently probe an endpoint to confirm it is where the approval says it is. What this control guarantees is that the decision was made in advance by somebody named and can be read back afterwards — not that the network was inspected.
- supabase/migrations/20260820100000_provider_credentials.sql
- supabase/migrations/20260820200000_ai_credential_repair.sql
- src/lib/harness/model.ts
Trail and retention
The decision trail, retention and deletion
Live
Each actor is taken from the authenticated session rather than supplied by the client.
Every act that changes the register’s claims is written to an append-only trail. Deletion is blocked for workspace users, owners and privileged platform roles, and the release proof verifies that boundary by attempting the deletion.
The trail is retained for the life of the tenancy and does not roll off on a fixed short-term schedule.
Procurement scope: read-access logging, scheduled machine-readable exports, certified deletion and litigation hold are not active controls. They can be scoped as enterprise requirements; the page does not represent them as implemented. Proposed
PHI, PCI and PII
Clinical, payment and personal data
Proposed
That scope is a design and contractual boundary, not a technical prevention control.
Portfolio governance does not require protected health information (PHI) or payment-card data. Product fields and import formats are designed for portfolio records, requirements, citations, test outcomes, findings and dispositions.
Onboarding acknowledgement, a maintained ingestion boundary and pattern screening for bulk imports are planned, not active. The screen described under agent authority covers the free text an agent’s permission and refusal records carry; it is not a general ingestion control and this page does not present it as one.
Enterprise review: confirm data classification, ingestion controls and any required business associate agreement before a regulated-data use case is activated.
Disclosure and incidents
Reporting a vulnerability, and incident response
Live
Both were proved before they were printed: an address that bounces silently turns a researcher trying to help into one who concludes nobody is listening.
There is a monitored security mailbox and a published disclosure policy, and the mailbox below was verified end to end with a real message before this page named it.
Security contact: security@lockedinlabs.ai — also published machine-readably in security.txt (RFC 9116), which carries the same address, the security page as its policy link, and the scope notes. There is no bug bounty, and the file says so rather than letting a researcher assume one. Tenant isolation is in scope and is the finding we most want.
What is not committed: a notification deadline. Escalation contacts, breach-notification timing and response obligations are defined in the applicable enterprise agreement, and until those operating commitments are executed for a tenancy this page makes no fixed response-time promise. A reviewer who needs “notify within N hours” in writing should raise it in the agreement, not read it into this section. Proposed
Platform operations
Platform operations access
Not held
Enterprise review should define approved access, logging and break-glass expectations.
Authorized platform operations use an infrastructure-layer credential that can bypass per-tenant policy for service operation and support. Customer-visible break-glass records and a named system actor are planned. Proposed
Availability
Availability, backup and known advisories
Live
Current dispositions for any advisory triaged as unreachable are available during a security review.
- BackupsDaily encrypted backups with point-in-time recovery, managed at the database platform layer.
- Dependency postureDependency advisories are triaged for reachability. Tests preserve any boundary used to classify an advisory as unreachable.
What is not committed. There is no published uptime target, no recovery time or recovery point objective, no status page and no disaster-recovery runbook that has been rehearsed end to end. The backups above are a platform feature that is switched on; a commitment about how fast and how recent a restore would be is a contract term, and this deployment has none. Recovery objectives are established in commercial terms for the estate in question. Proposed
Procurement readiness
Procurement and assurance readiness
Not held
There is no third-party audit artifact of any kind: the evidence on this page is our own measurement, run on a schedule, with the date it was taken printed beside it.
LockedIn Labs FDE currently provides the implemented control descriptions and dated evidence above for technical review. SOC 2 Type II, HITRUST and FedRAMP authorization are not currently held. Neither is ISO/IEC 27001, and the product is not PCI DSS assessed — it stores no card data, because card details are entered on Stripe’s own page and never reach this application. No independent penetration test report exists to share.
Under data-protection law the customer organization is the controller of its register content and LockedIn Labs FDE is its processor. A data processing agreement, standard contractual clauses, a business associate agreement (BAA), incident notification terms, support commitments and deletion undertakings are negotiated during enterprise review and defined in the executed agreement — they are contract terms, not features already switched on, and this page does not represent any of them as in force. Data residency is the one item on that list we will not contract for today: the regions under Encryption are the ones in use, and there is no EU or UK region to offer.
Directory provisioning (SCIM 2.0) is not implemented. It is named here because every enterprise procurement asks, and because the alternative — joiners and leavers handled by invitation and by an administrator remembering to revoke — is the honest description of what happens today. What it would take is a SCIM endpoint accepting the standard user and group operations against a per-tenant bearer credential, mapped onto the membership grades this page already describes, with a de-provisioning path that revokes rather than deletes so the decision trail survives the person leaving. It is a scoped piece of work, not a research problem, and it is not started.
The platform does not issue compliance assurances; nothing it produces is described as certified.
What to do with this
Anything above can be checked. Anything missing can be asked.
Every mechanism on this page names the files that carry it, so a reviewer can open them instead of taking our word. Send whatever this packet does not answer to the security team and you will get a direct answer rather than a deflection.