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.
The security overviewHow to read this page
Every control carries one of three standings
The same three the product uses inside a workspace. A word and a shape, never a color alone.
- Live
- Implemented and running in production today.
- Proposed
- Written down as a requirement. Not built. Do not plan around it.
- Not held
- We 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 Engineering External hosted service. Recorded 26 August 2026, 50 days ago: the run recorded no readable foreign row or accepted foreign write. It has not been re-run against this deployment. No Agentic Engineering Platform production verification is established by this proof. Schema measurement · LockedIn Labs Engineering hosted service. Measured on 4 October 2026, 11 days ago, with read-only queries against the production database catalogue. At that measurement, every application table had Row Level Security enabled and forced; no policy was unconditional and no table lacked a policy; the unauthenticated role held no privilege in the application schema; every view ran with the privileges of its caller; every function that runs as its owner pinned its search path; cross-organization references were declared as organization-and-record pairs. This account is built from that measurement rather than from a live query. Workspace security settings show current session and schema reads separately.
Where it is writtenscripts/prove-tenancy.mjsscripts/measure-posture.mjssrc/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 rest
- The database and its backups are stored encrypted with AES-256 by the managed Postgres platform.
- In transit
- Every 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 sits
- The application database and its backups are stored in the United States. 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. Processing by approved model providers, email services and the optional voice assistant follows the arrangements described under subprocessors and models and egress.
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 stores or logs a password or a verification code: the credential is exchanged with the auth service, passed straight through when a password is set or changed, 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 accounts
- A 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 door
- Enrollment issues ten single-use recovery codes, shown once and stored only as digests. They are not emailed. Nobody at Agentic Engineering Platform 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 lifetime
- Eight 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 acts
- Changing 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
- Sessions
- Held 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 database
- Administering 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 setting
- A 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. At least twelve characters, with a lower-case letter, an upper-case letter and a digit, and every new password is checked against the Have I Been Pwned breach corpus and refused if it appears there. The auth service applies this whenever a password is set or changed, so a password chosen before the policy took effect keeps working until it is next changed. 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.
The governed model gateway
Terminal tokens, and what the gateway checks on every call
Live
Claude and Codex are pointed at the project’s gateway address with a personal terminal token, and every call that arrives is checked. The six refusals it answers with are listed on the security overview.
- Off until a workspace turns it on
- The gateway answers nothing for a workspace until a workspace owner turns it on in Settings.
- The token is the whole credential
- A terminal token belongs to one person. It is shown once when it is minted and stored only as a digest, and it expires: thirty days unless its owner chooses another term, and never longer than a year. Its owner can revoke it from the workspace’s member settings, and a revoked token is refused on its next call. Proposal records keep the token’s identifier, never the credential.
- Who a token stops working for
- A revoked or expired token, a member who has left or been demoted, a loss of admission to Engineering, and a project the token may not reach are each refused, and the check runs again on every call rather than once when the token was minted.
- Minting needs the second factor
- Where the person’s account has a second factor, or their organization requires one, minting needs a session confirmed with it; a password alone, used directly against the API, cannot mint one. An account with no factor, in an organization that requires none, can mint with a password, which is the current default on this deployment.
- Rates are per token
- Gateway calls are rate limited per token by the minute. The proposal routes — ask, plan, change and challenge — are limited per token by the minute and by the UTC day, which bounds what one token can spend while nobody is watching.
- Content is classified before it is routed
- Each request is classified on the server from the exact text that would be sent: internal content, personal identifiers, health identifiers, or a secret. The class narrows the connections a request can reach and never widens them. A secret is refused before any model connection is opened, and no connection can be approved to carry one.
- Workspace policy governs model routing
- A connection must support the requested API and be approved by the workspace or use an operator-granted private network. It must also be eligible for the request’s content class. Project access is checked separately against the personal token.
- Egress is checked before connecting
- An approved provider is still refused when its endpoint falls outside the workspace’s network policy. An endpoint on a private network is routed only when the operator has granted that workspace that address.
- Spend is reserved first
- The remaining model budget is reserved before the call is made. A request the budget cannot cover is refused, and the reservation is settled when the call finishes.
- What is recorded
- Each reservation records the route, the content class, which classifier decided it and how many times each detector fired — a database constraint refuses anything but a number there — and the tokens and cost that settle it. The request itself is not part of that record.
Where this stops. A terminal token is presented without a second factor, by design: where one is required, the second factor is needed to mint it, not to use it, so a token is as strong as the place it is kept and the person’s ability to revoke it. Tokens minted before the mint rule required the second factor stay valid until they expire or are revoked.
What is outside the gateway. The gateway is the route a person configures; nothing forces Claude or Codex through it. Work delegated to a runner you enrol does not use it: the runner holds its own credentials on your machine, as described under where the work runs, below.
The gateway cannot inspect images or documents. A request that carries them is routed only to a connection approved for the highest content class it can carry, health identifiers, because nothing can be said about what is inside them. What an approved provider does with a request after it arrives is governed by the workspace’s own arrangement with that provider.
Where it is writtensrc/lib/ai/gateway.tssrc/lib/ai/classification.tssrc/lib/ai/key.tssrc/lib/integrations/egress.tssupabase/migrations/20261018040000_terminal_token_mint_requires_mfa.sqlsupabase/migrations/20261018500000_model_gateway_workspace_optin.sqlsupabase/migrations/20261018520000_terminal_propose_authority.sqlsupabase/migrations/20260918160000_ai_content_classification.sql
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.
Where the work runs, and who decides what ships
Delegate a work item to a registered agent. A runner you enrol on your own machine runs it with credentials that never leave that machine, tests the change with your repository's suite and has a second agent review it. The hosted service holds no model key for the run and never merges.
Acceptance checks the project rules, unresolved questions and required independent review. The requester cannot decide their own proposal, even with an approval role.
What a run is held to, and what came back
Operating policies ship with the product as catalog content, and a workspace adopts each one at an enforcement level it chooses. Each policy below ships either blocking or advisory, and an advisory policy reports 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-01- An agent may not grant itself authorityShips blocking
IL-02- A result is a proposal until a person accepts itShips blocking
IL-03- No opaque executionShips blocking
IL-04- Regulated content stays inside its approved boundaryShips blocking
IL-05- A gate seals before a deployShips blocking
IL-06- Every run names the model it ran underShips advisory
IL-07- A waiver names a personShips blocking
IL-08- A run narrates what it didShips advisory
IL-09- Secrets 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.
Where it is writtensupabase/migrations/20260820190000_tool_permission.sqlsrc/app/api/mcp/route.tssrc/lib/tool-permission/gate.tssupabase/migrations/20260820020000_agentic_harness.sql
Models and egress
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 premises
- Runs on hardware this organization controls. Prompts do not leave the network.
- Private cloud
- A tenant-isolated deployment under contract. Handling is governed by that agreement rather than public terms.
- Vendor hosted
- A 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. The register never holds a key — only where one lives, in words — and key-shaped text is refused at write by the database, not by a form.
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.
Where it is writtensupabase/migrations/20260820100000_provider_credentials.sqlsupabase/migrations/20260820200000_ai_credential_repair.sqlsrc/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 workspace’s record 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.
Evidence receipts and the published key
A receipt is an Ed25519 signature over canonical bytes. It holds the counts with their denominators, the acceptances, and the commit the export was pinned to, and the actors in it are hashed rather than named, because a document a stranger can verify must not be a directory of who works there. A claim whose count exceeds its own denominator is not rounded: the receipt is not issued.
A receipt carries its own public key, so a signature alone proves only that some key signed it, and anyone can mint a key and sign a body shaped like ours. What decides attribution is this deployment’s published list: /verify names Agentic Engineering Platform as the issuer only when the key that verified the signature is one published at /.well-known/receipt-keys.json, and otherwise says the signer is not recognized. Retired keys stay published with the date they stopped signing, so older receipts keep verifying.
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
Where it is writtensrc/lib/evidence/receipt.tssrc/lib/evidence/issuer.tssrc/lib/evidence/receipt-keys.tssrc/app/.well-known/receipt-keys.json/route.tssrc/app/verify/page.tsx
PHI, PCI and PII
Clinical, payment and personal data
Proposed
That scope is a design and contractual boundary, not a technical prevention control.
Engineering work does not require protected health information (PHI) or payment-card data. Product fields and import formats are designed for engineering records: projects, requirements, change proposals, review findings, test outcomes and decisions.
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, and the gateway’s classification decides which models may receive a request; neither is a general ingestion control and this page does not present either 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
Report vulnerabilities through the LockedIn Labs security contact form. Select Security or privacy and include the affected product.
Security contact: Report a security issue — also published machine-readably in security.txt (RFC 9116), which carries the same reporting route, 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.
- Backups
- Daily encrypted database backups are managed by Supabase. Point-in-time recovery is not enabled on this deployment. These backups include stored file metadata, but not the file contents held in Storage.
- Dependency posture
- Dependency 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 dated measurements.
Agentic Engineering Platform 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 workspace content and Agentic Engineering Platform 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
Evidence for your security review
Source references identify each implementation for technical review. Send questions this control detail does not answer to the security team.
Every claim on this page carries a standing: live, proposed, or not held. Anything it does not answer can go to the security contact.