standardThe Codices

STD-S — The Security Standard

Identity, authorization, secrets, and audit.

Authority rank
4
Version
v1.0
Adopted
2026-04-01
Held by
Stewardship Office
System
SYS-01

Source · docs/standards/STD-S-security.md · registered by rule

Subordinate to Codex 1 and the Codices named in §1. See 00-index.md for authority, normative vocabulary, citation format, and waiver rules, which this Standard does not restate.

1. Purpose

1.1 This Standard makes Codex 0 Chapter 08's deny-by-default, server-side, data-layer enforcement model checkable by a reviewer who did not build the feature under review. It fixes the mechanisms — has_role, RLS, GRANTs, elevation, secrets, audit, incident response — into obligations a second person can verify without re-reading the code.

1.2 It exists because a permission rule stated only as intent is not a permission rule. Every clause below names what a reviewer inspects, and what a pass or fail looks like.

2. Scope and non-scope

2.1 In scope: authorization enforcement (server and data layer), the has_role pattern, RLS and GRANT obligations, least privilege, elevation, separation of authority, service and machine identities, secret handling, transport and storage protection, input validation and output encoding, audit record content and retention, logging prohibitions, dependency and supply-chain obligations, vulnerability handling and incident procedure, and review cadence.

2.2 Not in scope: the role and scope model's conceptual definition (Codex 0 §08.3, restated here only where a clause depends on it); UI presentation of access states (STD-X); AI-specific data-access rules beyond what §5 of this Standard states generally (STD-I); data modeling and retention of non-audit tables (STD-D).

2.3 This Standard binds every table, function, endpoint, server function, and worker route that touches member, financial, or institutional data, regardless of which division or repository owns it.

3. Normative clauses

3.1 Deny by default

3.1.1 A new table, server function, or route must have an explicit access decision recorded before merge. A reviewer must be able to find, for any given table or endpoint, the affirmative grant that permits access; absence of a decision is a fail, not a pass-by-omission.

3.1.2 A table, function, or route that a reviewer cannot show is closed by default (e.g., a Postgres table with RLS disabled, or a server function with no auth check on its first line) must not ship.

3.2 Authorization is server-side and data-layer, never UI-only

3.2.1 Every authorization decision must be enforced in server code (a createServerFn handler or worker route) independent of any client-side check. A reviewer must be able to reproduce a denial by calling the server function or route directly, bypassing the UI, and observing the same refusal.

3.2.2 Every table holding member, financial, or institutional data must carry a row-level security policy enforcing the same rule the server-side check enforces. A reviewer must find both layers independently deny the same unauthorized request; a hidden button, greyed-out control, or client-side route guard must not be the only barrier present.

3.2.3 Where the server-side check and the RLS policy would permit different outcomes for the same request, this is a defect and must be corrected before merge; the two layers must agree.

3.3 The `has_role` pattern

3.3.1 Every authorization decision keyed to a role and scope must call the shared has_role(person, role, scope) security-definer function. A reviewer must find no bespoke, inline role-check logic duplicated in application code or in a policy definition; a duplicate implementation is a defect regardless of whether it currently agrees with has_role.

3.3.2 has_role must be defined security definer and must re-derive its answer from the Role table on every call; it must not accept a client-supplied role or scope claim as authoritative. A reviewer must find no code path where a JWT claim, session field, or request parameter is treated as sufficient proof of role without a has_role call.

3.3.3 has_role must return denied on any unresolvable input — an ambiguous scope reference, an unavailable dependency, a malformed role name. A reviewer must find a test exercising at least one such failure mode and asserting denial.

3.3.4 Roles must never be stored as a column on a profile or person table. A reviewer must find role and scope data only in a dedicated table (or tables) separate from Person/profile storage; a role-like boolean or enum column on a profile table is a fail.

3.4 RLS and GRANTs

3.4.1 Every migration that creates or alters a publicly reachable table must ship, in the same migration, both the table's RLS policies and its GRANT statements. A reviewer must find no migration that creates a table and defers its RLS or GRANTs to a later migration.

3.4.2 Every access class named in Codex 0 §04.4 (public, self, cohort, organization, division, institution) that applies to a table must have a corresponding RLS policy naming that class's condition explicitly. A reviewer must be able to map each policy to the access class it implements.

3.4.3 A GRANT must never exceed what the corresponding RLS policy would allow through; a table must not be grantable to a role at a privilege level (e.g., unrestricted SELECT) that its RLS policies do not also restrict. A reviewer checks that GRANT and RLS are read together, not that GRANT alone is treated as sufficient.

3.4.4 A table's RLS policy must call has_role or an equivalent security-definer function rather than re-implementing role or scope logic inline in the USING/WITH CHECK clause. A reviewer must find no policy containing its own bespoke join or subquery replicating what has_role already answers.

3.5 Least privilege and scope minimisation

3.5.1 A role grant must specify the narrowest scope (individual, cohort, organization, division, institution) that satisfies the stated purpose. A reviewer rejects a grant request or migration that defaults to a broader scope (e.g., institution) where a narrower one (e.g., cohort) would serve the stated function.

3.5.2 A service or agent identity's declared permitted-data set must be the minimum needed for its declared purpose. A reviewer must find the identity's registry entry (AGT- or service equivalent) enumerating exactly the entities it reads, with no unused or speculative grants.

3.5.3 An institution-wide grant must never be issued as a matter of convenience where an organization- or division-scoped grant would serve; a reviewer requires a stated reason in the grant's record whenever scope is institution-wide.

3.6 Elevation

3.6.1 Every elevated access (a steward, administrator, or engineer reading data outside their standing scope) must have a stated, specific reason recorded at or before the moment of access. A reviewer must find the reason text is not empty, generic, or reused verbatim across unrelated access events; a reused boilerplate reason is a fail (Codex 0 §08.6.5).

3.6.2 Every elevation must produce an AuditRecord and must carry an expiry; a reviewer must find no elevation grant lacking an expiry timestamp, and must find the elevated access is refused once the expiry has passed.

3.6.3 The subject of an elevated access must be able to retrieve the fact of that access and its stated reason through their own data-rights surface. A reviewer must exercise this retrieval path and confirm the elevation record appears.

3.7 Separation of authority

3.7.1 Administering a role grant (creating, modifying, or revoking it) must not itself confer read access to the data that grant reaches. A reviewer must find the administrator role's own permitted-data set excludes member content, and that the administrator must separately elevate (§3.6) to read it.

3.7.2 Seniority, employment duration, and job title must never appear as a condition in an RLS policy, a has_role check, or an authorization branch in server code. A reviewer greps for and rejects any check keyed to a title, tenure field, or "is executive" flag standing in for a role/scope grant.

3.7.3 Where a workflow would let one role both create and be the sole approver of its own consequential action (e.g., granting oneself an elevation), the roles must be split so a second, distinct human role acts. A reviewer must find no single role capable of executing both halves of such a pair unassisted.

3.8 Service and machine identities

3.8.1 Every service or agent identity must hold its own declared, scoped grant recorded in the Registry; it must not inherit ambient privilege from its runtime environment (a shared database superuser, a worker's default binding). A reviewer must find the identity's credential is scoped, not the platform's default connection.

3.8.2 A service or agent identity must own no data of its own; every table it writes to must attribute the write to the person or purpose it acts on behalf of. A reviewer checks that write paths record an actor and, where applicable, a distinct subject, not only the service identity.

3.8.3 An agent identity's data access must be computed at request time from its invoking person's current permissions, never cached across a session in a way that could outlive a permission change. A reviewer must find the access check is re-evaluated per request, not memoized for the session's duration.

3.9 Secret handling

3.9.1 A secret (API key, service credential, signing key) must never appear in client-bundled code, in a SURF-/DASH- shipped bundle, or in any artifact reachable by a browser. A reviewer inspects the built client bundle or the source of any client-executed module and confirms no secret literal or import of a secret-holding module is present.

3.9.2 A secret must never appear in a log line, error message, or stack trace surfaced to any sink (console, log aggregator, error tracker). A reviewer must find log statements do not interpolate secret-bearing variables, and that error handlers redact or omit them.

3.9.3 A secret must be read inside the request handler at the point of use, never captured at module scope where it would be evaluated once at cold-start and potentially cached beyond its validity or across tenants. A reviewer greps for environment reads occurring outside handler functions and flags them.

3.9.4 Every secret must have a stated rotation procedure and owner; a reviewer must be able to name, for any secret in use, who rotates it and on what trigger (schedule, suspected compromise, personnel departure).

3.10 Transport and storage protection

3.10.1 All traffic between client, edge worker, and database must be encrypted in transit; a reviewer confirms no endpoint accepts a plaintext connection.

3.10.2 Data classified as member-identifying or financial must be encrypted at rest by the storage layer's standard mechanism; a reviewer confirms the storage configuration for such tables has encryption at rest enabled, not merely assumed from the platform default without verification.

3.10.3 A credential (password, token) must never be stored in plaintext or in a reversible form where a one-way hash would serve; a reviewer inspects the schema and confirms the column type and write path use the institution's standard hashing mechanism.

3.11 Input validation and output encoding

3.11.1 Every server function and route accepting external input must validate that input's shape and type before use; a reviewer must find a validation step (schema check or equivalent) preceding any database write or query built from that input.

3.11.2 Every database query built from external input must use parameterized queries or an equivalent safe-construction method; a reviewer must find no string concatenation of unvalidated input into a query.

3.11.3 Output rendered to a user must be encoded for its output context (HTML, attribute, URL) so that stored or reflected input cannot execute as markup or script; a reviewer confirms the rendering path uses the framework's escaping mechanism rather than an unescaped raw-insertion method for any value that can originate from user input.

3.12 Audit records

3.12.1 Every consequential action (Codex 0 §13.3.3's illustrative list and its equivalents outside AI) must produce an AuditRecord containing: the actor, the subject, the action taken, the time, the stated reason where the action is an elevation or consent-derived access, and the before/after state where the action modifies a record. A reviewer checks a sample of consequential actions against this list and fails any missing field.

3.12.2 An AuditRecord, once written, must be immutable: no update or delete path may exist against the audit table for any role except the retention procedure of §3.13. A reviewer confirms no application code path issues an UPDATE or DELETE against the audit table, and that RLS on the table forbids it structurally.

3.12.3 An audit record must be attributable to a specific, resolvable actor identity (a Person, a service identity, or an AGT- agent identity) — never to a generic system account that obscures which caller acted. A reviewer rejects any audit entry whose actor field cannot be resolved to a named identity.

3.13 Retention of audit records

3.13.1 Audit records must be retained for the period set by the Stewardship Office's retention schedule and must survive the deletion or severance of the subject's other personal data (Codex 0 §04.3.1.3, §04.8.4). A reviewer confirms a deletion request against a Person does not remove or anonymize the audit trail referencing that person's data.

3.13.2 A revoked role, elevation, or delegation's historical audit trail must remain queryable after revocation; a reviewer confirms revocation removes future access without deleting the historical record that the grant existed (Codex 0 §08.10.4).

3.14 Logging prohibitions

3.14.1 A log entry must never contain a secret (§3.9.2) or the substantive content of member work (a submission body, an assessment narrative, financial figures, a private message). A reviewer samples production-bound log statements touching these data classes and fails any that log the content itself rather than an identifier or event descriptor.

3.14.2 A log entry may contain identifiers (person ID, record ID, action type) needed to correlate with the audit trail, but must not substitute for the audit record; a reviewer must find consequential actions produce an AuditRecord, not merely a log line.

3.15 Dependency and supply-chain obligations

3.15.1 Every third-party package added to the codebase must be recorded through the project's standard dependency-management flow (bun add/equivalent lockfile update); a reviewer rejects a dependency introduced by manual file edit without a corresponding lockfile change.

3.15.2 Dependencies must be scanned for known vulnerabilities on a fixed cadence set by the owning division, and a dependency with a known critical vulnerability and an available fix must be updated before the next release; a reviewer checks the most recent scan result against the currently pinned versions.

3.15.3 A dependency granted filesystem, network, or credential access beyond what its declared function requires must not be added without a recorded justification; a reviewer checks the justification exists for any dependency operating outside a narrow, well-known library category.

3.16 Vulnerability handling and incident procedure

3.16.1 A discovered vulnerability must be triaged within one business day of report and assigned a severity; a reviewer checks the triage record's timestamp against the report timestamp.

3.16.2 A confirmed vulnerability that exposes member data must trigger the incident procedure: containment, a written timeline of the exposure window, identification of affected subjects, and notification to affected subjects and to the Stewardship Office, each with a recorded completion time. A reviewer checks that all four steps have timestamps and are not marked complete without evidence.

3.16.3 Notification to affected subjects must occur without undue delay once the exposure and its scope are confirmed, and no later than the period fixed by the institution's incident procedure record; a reviewer checks the elapsed time between confirmation and notification against that fixed period.

3.16.4 An incident's post-resolution record must name the root cause, the fix, and the specific clause of this Standard (if any) whose absence or violation contributed to the incident, so that a pattern of incidents against the same clause is visible (per STD index §7.2's amendment trigger).

3.17 Penetration and access review cadence

3.17.1 A penetration test or equivalent adversarial review must occur at the cadence set by the Stewardship Office and after any material architectural change to the authorization or data layer; a reviewer checks the date of the last test against that cadence.

3.17.2 Access review (Codex 0 §08.10) must occur at the fixed cadence set by the Stewardship Office and additionally on every material change of responsibility; a reviewer checks, for any active grant, that its last review date is within the cadence window and that a role change or departure triggered an off-cycle review where applicable.

3.17.3 A grant that cannot answer, on request, who holds it, on what scope, granted by whom, when, why, and when last exercised (Codex 0 §08.10.3) is treated as unreviewed and is a defect regardless of the calendar cadence.

4. Governing Codex clauses

4.1 Codex 0 Chapter 08 (permissions, has_role, elevation, separation of duties, service and agent identities, review cadence) — this Standard's primary source.

4.2 Codex 0 Chapter 04 §04.4, §04.8 (visibility contract, data rights, access transparency).

4.3 Codex 1 Article V (member is not the product), Article VI.3, VI.5, VI.6 (human oversight, roles enforced server-side and stored in dedicated tables, auditability).

4.4 Codex 7 (non-functional requirements: security, auditing, logging, permissions; platform constraints: createServerFn, edge worker runtime, GRANTs and RLS in the same migration, environment values read inside handlers).

5. Conformance checklist

  • [ ] Every new table/function/route has a recorded, affirmative access decision (§3.1.1).
  • [ ] No table or route relies on RLS-disabled or unauthenticated-by-default state (§3.1.2).
  • [ ] Server-side check reproducible by direct call, independent of UI (§3.2.1).
  • [ ] Matching RLS policy exists for every server-side authorization rule (§3.2.2).
  • [ ] Server-side and RLS layers agree on outcome for the same request (§3.2.3).
  • [ ] All role/scope checks route through has_role; no bespoke duplicate logic (§3.3.1).
  • [ ] has_role is security definer, re-derives from Role table, ignores client claims (§3.3.2).
  • [ ] has_role denies on unresolved input, with a test proving it (§3.3.3).
  • [ ] No role/scope column exists on any profile or person table (§3.3.4).
  • [ ] Every table migration ships RLS and GRANTs together (§3.4.1).
  • [ ] Each applicable access class has a named RLS policy (§3.4.2).
  • [ ] GRANT does not exceed RLS-permitted access (§3.4.3).
  • [ ] RLS policies call has_role rather than reimplementing checks (§3.4.4).
  • [ ] Grants use the narrowest workable scope, with reasons for institution-wide grants (§3.5).
  • [ ] Elevation reasons are specific, non-boilerplate, time-bounded, and audited (§3.6).
  • [ ] Administering access does not itself grant read access (§3.7.1).
  • [ ] No authorization branch keyed to seniority, title, or tenure (§3.7.2).
  • [ ] No single role can both create and approve its own consequential elevation (§3.7.3).
  • [ ] Service/agent identities hold scoped, Registry-declared grants, not ambient privilege (§3.8).
  • [ ] No secret appears in client bundle, logs, or module-scope evaluation (§3.9).
  • [ ] Rotation owner and trigger named for every secret (§3.9.4).
  • [ ] Transport encrypted; sensitive data encrypted at rest; credentials hashed (§3.10).
  • [ ] External input validated before use; queries parameterized; output encoded (§3.11).
  • [ ] Consequential actions produce complete, immutable AuditRecords (§3.12).
  • [ ] Audit records survive subject deletion and revocation events (§3.13).
  • [ ] No secrets or member content body text in logs (§3.14).
  • [ ] Dependencies added via lockfile flow, scanned on cadence, justified if privileged (§3.15).
  • [ ] Vulnerability triage, containment, notification timelines met and recorded (§3.16).
  • [ ] Penetration test and access review both current per fixed cadence (§3.17).
  • [ ] not-applicable-because: state the reason for any line not applicable to this piece of work.

6. Interfaces to other Standards

6.1 STD-A: engine and boundary design must route every data access through the layers this Standard governs; STD-A does not restate authorization mechanics.

6.2 STD-D: table modeling, migrations, and retention obligations for non-audit data are governed by STD-D; audit-record retention specifically is governed here (§3.13).

6.3 STD-I: an agent's data access is a subset of this Standard's service/agent identity rules (§3.8), constrained further by STD-I's subject-visibility and human-checkpoint clauses.

6.4 STD-Q: this Standard's checklist is one input the STD-Q gate requires before anything ships.

7. Open questions

7.1 The exact fixed cadence for penetration testing and access review (§3.17) awaits the Stewardship Office's determination (Codex 0 §08.15.1).

7.2 Whether cross-division elevation auto-expires at a division boundary or must be separately scoped (Codex 0 §08.15.2) is unresolved and affects §3.6's expiry mechanics.

7.3 The fixed notification-delay ceiling referenced in §3.16.3 is not yet numerically set; it is to be fixed by decision record and cited here once determined.

7.4 Whether a single incident-response owner role exists institution-wide or is division-specific is undecided.