Files

1797 lines
115 KiB
JavaScript

// @hanzo/trust — goja bundle (GENERATED by goja/build.mjs; edit src/*.ts + controls.json).
// Self-contained, ESM-free; runs in dop251/goja inside hanzoai/cloud (HIP-0106).
"use strict";
(() => {
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true,
writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
// <define:__CONTROLS__>
var define_CONTROLS_default = [{ id: "iam.issuer", category: "access", claim: "Hanzo IAM issues ev\
ery credential, and signing in is an OIDC redirect rather than a form any application holds.", mechanism: "\
The authorization endpoint validates the request and returns a code; a single mint path turns that c\
ode into tokens. Everything else in the estate consumes tokens rather than issuing them.", status: "\
partial", note: "Partial for scope, not for quality. The redirect flow and the single mint path are \
both there to read. Establishing that NOTHING else in the estate issues a credential means auditing \
every service for a second path, and that sweep has not been done \u2014 so the narrower word is the hone\
st one.", enforced: [{ repo: "hanzoai/iam", path: "internal/oidc/authorize.go", symbol: "Authorize" },
{ repo: "hanzoai/iam", path: "internal/oidc/mint.go", symbol: "mint" }], verified: [{ method: "rea\
d", at: [{ repo: "hanzoai/iam", path: "internal/oidc/authorize.go", symbol: "Authorize" }, { repo: "\
hanzoai/iam", path: "internal/oidc/mint.go", symbol: "mint" }], detail: "The redirect flow and the s\
ingle mint choke point were read in the source. Rows under issue-user-token, mint-user-keys, revoke-\
user-keys, token-exchange evidence this. Every credential the platform issues, rotates or revokes le\
aves a row under a reserved action." }, { method: "audit", actions: ["issue-user-token", "mint-user-\
keys", "revoke-user-keys", "token-exchange"] }], maps: [{ clause: "soc2:CC6.1", strength: "partial" },
{ clause: "soc2:CC6.2", strength: "partial" }, { clause: "soc2:CC6.3", strength: "partial" }, { clause: "\
iso27001:A.5.15", strength: "partial" }, { clause: "iso27001:A.5.16", strength: "partial" }, { clause: "\
iso27001:A.5.17", strength: "partial" }, { clause: "nist80053:IA", strength: "partial" }, { clause: "\
nist80053:AC", strength: "partial" }] }, { id: "iam.pkce.s256", category: "access", claim: "A client\
with no registered secret must present a challenge, and only the hashed method is accepted \u2014 offeri\
ng the plain method is refused rather than quietly downgraded.", mechanism: "A public client arrivin\
g with no challenge is refused at the authorization endpoint. An explicitly supplied method other th\
an the hashed one is refused there, at the mint path, and at the token endpoint; the verifier compar\
es against a single constant. Discovery advertises the hashed method alone.", status: "automated", enforced: [
{ repo: "hanzoai/iam", path: "internal/oidc/authorize.go", symbol: "Authorize" }, { repo: "hanzoai\
/iam", path: "internal/oidc/pkce.go", symbol: "VerifyChallenge" }, { repo: "hanzoai/iam", path: "int\
ernal/oidc/oidc.go", symbol: "Discovery" }], verified: [{ method: "read", at: [{ repo: "hanzoai/iam",
path: "internal/oidc/authorize.go", symbol: "Authorize" }, { repo: "hanzoai/iam", path: "internal/\
oidc/pkce.go", symbol: "VerifyChallenge" }, { repo: "hanzoai/iam", path: "internal/oidc/oidc.go", symbol: "\
Discovery" }], detail: "Three refusal sites were read: the authorization endpoint, the mint path and\
the verifier. Two boundaries are worth knowing \u2014 an OMITTED method defaults to the hashed one rathe\
r than being refused, so the method parameter is optional while the challenge is not; and the device\
-code grant has no challenge branch at all." }], maps: [{ clause: "soc2:CC6.1", strength: "full" }, {
clause: "soc2:CC6.6", strength: "full" }, { clause: "iso27001:A.8.5", strength: "full" }, { clause: "\
iso27001:A.8.24", strength: "full" }, { clause: "nist80053:IA", strength: "full" }, { clause: "nist8\
0053:SC", strength: "full" }] }, { id: "iam.password.argon2id", category: "access", claim: "New pass\
words are hashed with a memory-hard function at 64 MiB and two passes with a random salt, the cost p\
arameters ride inside the stored digest, and an unrecognised scheme fails closed.", mechanism: "One \
credential package owns hashing. It writes a digest carrying memory, passes and parallelism, so rais\
ing the cost later does not lock anyone out. Verification reads the scheme off the row and refuses a\
nything outside the supported set; the terminal path returns false rather than falling through.", status: "\
automated", enforced: [{ repo: "hanzoai/iam", path: "internal/cred/cred.go", symbol: "CreateHash" },
{ repo: "hanzoai/iam", path: "internal/users/users.go", symbol: "CheckPassword" }], verified: [{ method: "\
read", at: [{ repo: "hanzoai/iam", path: "internal/cred/cred.go", symbol: "CreateHash" }, { repo: "h\
anzoai/iam", path: "internal/users/users.go", symbol: "CheckPassword" }], detail: "The parameter blo\
ck reads 64 MiB memory, 2 iterations, 16-byte salt, 32-byte key, and the digest is written in the st\
andard parameterised form. An older scheme remains an accepted VERIFY path so legacy rows still auth\
enticate; every new or updated digest uses the memory-hard one. The doc comment on the user record d\
escribes this backwards \u2014 the prose is stale, the code is not." }], maps: [{ clause: "soc2:CC6.\
1", strength: "full" }, { clause: "iso27001:A.5.17", strength: "full" }, { clause: "iso27001:A.8.24",
strength: "full" }, { clause: "nist80053:IA", strength: "full" }, { clause: "nist80053:SC", strength: "\
full" }] }, { id: "iam.token.algorithms", category: "access", claim: "Bearer tokens verify against a\
closed list of signing algorithms. Symmetric signing and the none algorithm are absent from it, so \
no forged header can select a path where the verifying key is also the signing key.", mechanism: "On\
e accepted-algorithms list is handed to the parser as its valid-methods option on the single verific\
ation path. The post-quantum signature method is registered against a real implementation, not a pla\
ceholder, and is in the default build.", status: "automated", enforced: [{ repo: "hanzoai/iam", path: "\
internal/oidc/verify.go", symbol: "acceptedAlgs" }, { repo: "hanzoai/iam", path: "internal/oidc/mlds\
a.go", symbol: "init" }, { repo: "hanzoai/iam", path: "internal/authz/authz_http_test.go" }], verified: [
{ method: "read", at: [{ repo: "hanzoai/iam", path: "internal/oidc/verify.go", symbol: "acceptedAl\
gs" }, { repo: "hanzoai/iam", path: "internal/oidc/mldsa.go", symbol: "init" }, { repo: "hanzoai/iam",
path: "internal/authz/authz_http_test.go" }], detail: "A test at internal/authz/authz_http_test.go\
asserts this. A test forges a symmetrically-signed token and asserts it is refused at parse. The li\
st carries two RSA variants, three NIST curves and one lattice scheme; the remaining RSA variants an\
d the probabilistic-padding series are not accepted. Tokens from a federated provider are checked ag\
ainst a SEPARATE closed list with slightly different members." }], maps: [{ clause: "soc2:CC6.1", strength: "\
full" }, { clause: "soc2:CC6.6", strength: "full" }, { clause: "iso27001:A.8.5", strength: "full" },
{ clause: "iso27001:A.8.24", strength: "full" }, { clause: "nist80053:IA", strength: "full" }, { clause: "\
nist80053:SC", strength: "full" }] }, { id: "iam.refresh.rotation", category: "access", claim: "Refr\
esh tokens are single-use. Spending one mints a successor in the same family; presenting a spent one\
deletes every token in that family, the working successor included.", mechanism: "Redemption marks \
the row consumed before the successor is issued, and the successor inherits the family id set when t\
he grant was established. A request carrying an already-consumed token revokes the family and answer\
s with a grant error.", status: "automated", enforced: [{ repo: "hanzoai/iam", path: "internal/oidc/\
refresh.go", symbol: "Refresh" }], verified: [{ method: "read", at: [{ repo: "hanzoai/iam", path: "i\
nternal/oidc/refresh.go", symbol: "Refresh" }], detail: "The consumed flag is set before the success\
or is minted, and the family cascade DELETES rows rather than only flagging them. One caveat: the ca\
scade is best-effort \u2014 it swallows the list error and ignores each delete error while still refusing\
the caller \u2014 so a partial revocation fails quietly and only the caller's refusal is guaranteed." }],
maps: [{ clause: "soc2:CC6.1", strength: "full" }, { clause: "soc2:CC7.2", strength: "full" }, { clause: "\
iso27001:A.5.17", strength: "full" }, { clause: "iso27001:A.8.16", strength: "full" }, { clause: "ni\
st80053:IA", strength: "full" }, { clause: "nist80053:SI", strength: "full" }] }, { id: "iam.mfa.ses\
sions", category: "access", claim: "Adding a second factor, removing one, or changing which is prefe\
rred drops every other session and clears the remember-this-device window.", mechanism: "All three m\
utations run through one save path, which revokes every session but the calling one and blanks the r\
emember deadline and its digest. A session id pruned from the row is treated as revoked on its next \
resolve.", status: "automated", enforced: [{ repo: "hanzoai/iam", path: "internal/mfa/mfa.go", symbol: "\
save" }, { repo: "hanzoai/iam", path: "internal/sessions/resolve.go", symbol: "RevokeOthers" }], verified: [
{ method: "read", at: [{ repo: "hanzoai/iam", path: "internal/mfa/mfa.go", symbol: "save" }, { repo: "\
hanzoai/iam", path: "internal/sessions/resolve.go", symbol: "RevokeOthers" }], detail: "One shared s\
ave path revokes other sessions and clears the remember window. Worth knowing: this revokes BROWSER \
SESSIONS. Access and refresh tokens already outstanding for that user are not revoked here, so a tok\
en issued before the change keeps working until it expires or its family is revoked by another route\
." }], maps: [{ clause: "soc2:CC6.1", strength: "full" }, { clause: "soc2:CC6.3", strength: "full" },
{ clause: "iso27001:A.5.17", strength: "full" }, { clause: "iso27001:A.8.5", strength: "full" }, {
clause: "nist80053:IA", strength: "full" }, { clause: "nist80053:AC", strength: "full" }] }, { id: "\
iam.scim.users", category: "access", claim: "A directory provisions users over the standard protocol\
\u2014 create, update, replace, deactivate, delete, with filter and patch. Someone removed from your dir\
ectory loses their account by that act.", mechanism: "A provisioning surface routes the full user li\
fecycle and publishes its service-provider configuration, schemas and resource types for a client to\
discover.", status: "automated", enforced: [{ repo: "hanzoai/iam", path: "internal/scim/scim.go", symbol: "\
Route" }, { repo: "hanzoai/iam", path: "internal/scim/schemas.go", symbol: "ServiceProviderConfig" }],
verified: [{ method: "read", at: [{ repo: "hanzoai/iam", path: "internal/scim/scim.go", symbol: "R\
oute" }, { repo: "hanzoai/iam", path: "internal/scim/schemas.go", symbol: "ServiceProviderConfig" }],
detail: "The user lifecycle routes are all present. The published capabilities declare bulk, sort \
and etag as false, so a client is told what is missing rather than discovering it at runtime." }], maps: [
{ clause: "soc2:CC6.2", strength: "full" }, { clause: "soc2:CC6.3", strength: "full" }, { clause: "\
iso27001:A.5.16", strength: "full" }, { clause: "iso27001:A.5.18", strength: "full" }, { clause: "is\
o27001:A.6.5", strength: "full" }, { clause: "nist80053:AC", strength: "full" }, { clause: "nist8005\
3:PS", strength: "full" }] }, { id: "iam.scim.groups", category: "access", claim: "Group membership \
arrives from a customer directory and drives role assignment.", mechanism: "None. There is no group \
route, no handler, and no group resource type \u2014 the published resource-type set has exactly one memb\
er.", status: "absent", note: "Absent, not stubbed. Role assignment cannot be driven from a customer\
directory today: users provision, group membership does not. A request for the group collection ans\
wers a plain 404 rather than a protocol error document, because the published-resource path covers o\
nly schema and resource-type lookups.", enforced: [{ repo: "hanzoai/iam", path: "internal/scim/schem\
as.go", symbol: "resourceTypes" }], verified: [{ method: "read", at: [{ repo: "hanzoai/iam", path: "\
internal/scim/schemas.go", symbol: "resourceTypes" }], detail: "The resource-type list has one membe\
r, User. No group handler exists under any spelling." }], maps: [{ clause: "soc2:CC6.3", strength: "\
partial" }, { clause: "iso27001:A.5.18", strength: "partial" }, { clause: "nist80053:AC", strength: "\
partial" }] }, { id: "iam.passkey.signin", category: "access", claim: "A registered passkey can be u\
sed to sign in.", mechanism: "None in the running build. Passkey credentials can be registered and m\
anaged, and there is no assertion ceremony to challenge one \u2014 no begin-login route of any spelling e\
xists.", status: "absent", note: "The capability flag returns false and the login descriptor is mask\
ed, so no screen offers passkey sign-in. That is the only honest thing a login screen can do about a\
method the server cannot perform. Second factors that do work: an authenticator app, SMS and email,\
with recovery codes.", enforced: [{ repo: "hanzoai/iam", path: "pkg/schema/passkey.go", symbol: "Pa\
sskeySignin" }, { repo: "hanzoai/iam", path: "internal/webauthn/webauthn.go", symbol: "Route" }, { repo: "\
hanzoai/iam", path: "internal/oidc/frontdoor_passkey_test.go" }], verified: [{ method: "read", at: [
{ repo: "hanzoai/iam", path: "pkg/schema/passkey.go", symbol: "PasskeySignin" }, { repo: "hanzoai/\
iam", path: "internal/webauthn/webauthn.go", symbol: "Route" }, { repo: "hanzoai/iam", path: "intern\
al/oidc/frontdoor_passkey_test.go" }], detail: "A test at internal/oidc/frontdoor_passkey_test.go as\
serts this. A test names the four candidate ceremony paths and asserts all four answer 404. The cred\
ential surface offers registration and management only." }], maps: [{ clause: "soc2:CC6.1", strength: "\
partial" }, { clause: "iso27001:A.8.5", strength: "partial" }, { clause: "nist80053:IA", strength: "\
partial" }] }, { id: "authz.path.grant", category: "access", claim: "A resource has a path, and a gr\
ant covers a path and everything below it, so org-wide access, one workspace, and a narrowed agent c\
redential all fall out of one check.", mechanism: "One authorization function tests the requested pa\
th against the paths a caller holds, admitting a path and its descendants.", status: "partial", note: "\
The containment model is real and it is one function. What it is built on is a byte-by-byte prefix c\
omparison, which does not do what the model needs \u2014 that is recorded separately as authz.path.segmen\
t, and it is why this is partial rather than automated.", enforced: [{ repo: "hanzoai/iam", path: "i\
nternal/authz/authz.go", symbol: "pathAuthorized" }], verified: [{ method: "read", at: [{ repo: "han\
zoai/iam", path: "internal/authz/authz.go", symbol: "pathAuthorized" }], detail: "One function answe\
rs the containment question for every caller shape." }], maps: [{ clause: "soc2:CC6.1", strength: "p\
artial" }, { clause: "soc2:CC6.3", strength: "partial" }, { clause: "iso27001:A.5.15", strength: "pa\
rtial" }, { clause: "iso27001:A.5.18", strength: "partial" }, { clause: "iso27001:A.8.3", strength: "\
partial" }, { clause: "nist80053:AC", strength: "partial" }] }, { id: "authz.path.segment", category: "\
access", claim: "A grant on one path does not cover a longer path that merely starts with the same c\
haracters, because the comparison advances a segment at a time.", mechanism: "None. The comparison i\
s a byte prefix. The collisions someone already hit are handled by a separate exact-match table rath\
er than by making the comparison segment-wise.", status: "absent", note: "Recorded absent because th\
e mechanism named in the claim does not exist \u2014 not because path authorization is missing. The comme\
nt above the function records hitting this exact hazard, where one action name is a character prefix\
of another, and working around it with an exact-match map. That workaround covers the collisions so\
meone thought of; a segment walk would make the whole class impossible. A segment-wise implementatio\
n exists elsewhere in the estate but is not on this path.", enforced: [{ repo: "hanzoai/iam", path: "\
internal/authz/authz.go", symbol: "pathAuthorized" }], verified: [{ method: "read", at: [{ repo: "ha\
nzoai/iam", path: "internal/authz/authz.go", symbol: "pathAuthorized" }], detail: "The function body\
is a byte prefix test, and the comment above it documents the workaround." }], maps: [{ clause: "so\
c2:CC6.1", strength: "partial" }, { clause: "soc2:CC6.3", strength: "partial" }, { clause: "iso27001\
:A.5.15", strength: "partial" }, { clause: "iso27001:A.8.3", strength: "partial" }, { clause: "nist8\
0053:AC", strength: "partial" }] }, { id: "edge.identity.headers", category: "network", claim: "Head\
ers naming an organization, a user, an email or a role are stripped at the edge before a handler rea\
ds one, and identity is written back only from a verified token.", mechanism: "The request middlewar\
e deletes every authority header and every sub-scope header by name, then re-mints them from validat\
ed claims. A second layer deletes any header in two reserved families by prefix, so a new name canno\
t arrive unhandled.", status: "automated", enforced: [{ repo: "hanzoai/cloud", path: "middleware_ide\
ntity.go", symbol: "authorityHeaders" }, { repo: "hanzoai/base", path: "tools/claims/claims.go", symbol: "\
StripIdentityHeaders" }], verified: [{ method: "read", at: [{ repo: "hanzoai/cloud", path: "middlewa\
re_identity.go", symbol: "authorityHeaders" }, { repo: "hanzoai/base", path: "tools/claims/claims.go",
symbol: "StripIdentityHeaders" }], detail: "The deletes run before anything reads a header, and th\
e re-mint draws only from validated claims. The organization a caller asks to act in is honoured onl\
y where the signed membership already admits it; asking for one they are not in falls back to their \
own." }], maps: [{ clause: "soc2:CC6.1", strength: "full" }, { clause: "soc2:CC6.6", strength: "full" },
{ clause: "iso27001:A.8.3", strength: "full" }, { clause: "iso27001:A.8.20", strength: "full" }, {
clause: "nist80053:AC", strength: "full" }, { clause: "nist80053:SC", strength: "full" }] }, { id: "\
base.tenant.file", category: "data", claim: "An organization's data is its own database file, so a q\
uery cannot reach across organizations \u2014 there is no second file open to reach into.", mechanism: "\
One function is the only way a subsystem opens a per-organization database, and it builds the path f\
rom the organization slug. A subsystem cannot name a file outside its tenant because it never builds\
the path itself.", status: "automated", enforced: [{ repo: "hanzoai/cloud", path: "orgdb.go", symbol: "\
OpenOrgDB" }], verified: [{ method: "read", at: [{ repo: "hanzoai/cloud", path: "orgdb.go", symbol: "\
OpenOrgDB" }], detail: "The path is composed from the organization slug and the subsystem name; ever\
y caller goes through the one opener." }], maps: [{ clause: "soc2:CC6.1", strength: "full" }, { clause: "\
soc2:C1.1", strength: "full" }, { clause: "iso27001:A.8.3", strength: "full" }, { clause: "iso27001:\
A.8.12", strength: "full" }, { clause: "nist80053:AC", strength: "full" }, { clause: "nist80053:SC",
strength: "full" }] }, { id: "base.tenant.key", category: "data", claim: "Each database is opened \
under a key derived for that organization alone from one master, so a leaked key is worth one tenant\
instead of the estate.", mechanism: "A key is derived from one master through a standard derivation\
function, bound to the namespace that owns it. The derivation is a pure function of its inputs, so \
a file reopens after a restart with nothing kept beside it, and a master of the wrong length is an e\
rror rather than a quiet fall back to no key.", status: "partial", note: "Two different derivations \
answer to this claim and only one is per-organization. The data plane derives per organization and p\
er user, as the claim describes. The cloud file layer derives per FILE, from a random file id \u2014 so t\
wo files of the SAME organization have unrelated keys, and the key is not a function of the organiza\
tion. Per-file derivation is not weaker against a stolen file, it is stronger; it just does not supp\
ort a sentence that says the key belongs to the organization.", enforced: [{ repo: "hanzoai/base", path: "\
plugins/org/org_db.go", symbol: "orgDEK" }, { repo: "hanzoai/cloud", path: "cek/cek.go", symbol: "De\
rive" }], verified: [{ method: "read", at: [{ repo: "hanzoai/base", path: "plugins/org/org_db.go", symbol: "\
orgDEK" }, { repo: "hanzoai/cloud", path: "cek/cek.go", symbol: "Derive" }], detail: "Both derivatio\
ns were read side by side. They use the same primitive and bind different things into it." }], maps: [
{ clause: "soc2:CC6.1", strength: "partial" }, { clause: "soc2:C1.1", strength: "partial" }, { clause: "\
iso27001:A.8.24", strength: "partial" }, { clause: "iso27001:A.8.12", strength: "partial" }, { clause: "\
nist80053:SC", strength: "partial" }] }, { id: "audit.request", category: "infrastructure", claim: "\
One row records who acted, from where, the method and path, the action, the status the server return\
ed, and the time.", mechanism: "Request middleware writes one record per request from the validated \
principal and the response, into a table with a monotonic sequence. Credential-shaped path segments \
and the user agent are scrubbed before the row is written.", status: "automated", enforced: [{ repo: "\
hanzoai/cloud", path: "audit_middleware.go", symbol: "Audit" }, { repo: "hanzoai/cloud", path: "audi\
t/store.go", symbol: "Append" }, { repo: "hanzoai/cloud", path: "audit/record.go" }], verified: [{ method: "\
read", at: [{ repo: "hanzoai/cloud", path: "audit_middleware.go", symbol: "Audit" }, { repo: "hanzoa\
i/cloud", path: "audit/store.go", symbol: "Append" }, { repo: "hanzoai/cloud", path: "audit/record.g\
o" }], detail: "There are TWO trails and they are not the same shape. This one is per request. The i\
dentity service keeps a second record type carrying organization, user, address, method, request pat\
h, action, payload, response and status \u2014 but it is written for accountable actions rather than for \
every request, and its address field is deliberately left empty because behind the ingress the peer \
address is the ingress pod, which identifies nothing while still being personal data." }], maps: [{ clause: "\
soc2:CC7.2", strength: "full" }, { clause: "soc2:CC4.1", strength: "full" }, { clause: "iso27001:A.8\
.15", strength: "full" }, { clause: "iso27001:A.8.16", strength: "full" }, { clause: "nist80053:AU",
strength: "full" }] }, { id: "audit.redaction", category: "data", claim: "What the trail records i\
s scrubbed of secrets before it is written.", mechanism: "Payloads emitted with a record are walked \
recursively against a denylist and matching values are replaced with a marker. The request path is s\
crubbed of credential-shaped segments and the user agent is scrubbed, before the row is written.", status: "\
partial", note: "A Hanzo page describes this as the request body recorded with passwords masked. No \
request body is captured anywhere on this path: the middleware builds a row from method, path, statu\
s and principal only, and the identity service's two writers set the payload field explicitly. Redac\
tion is real; the body capture it is described as protecting does not happen. Nothing is leaking \u2014 t\
he sentence is wrong, and that is the defect this entry records.", enforced: [{ repo: "hanzoai/cloud",
path: "audit/redact.go", symbol: "Redact" }, { repo: "hanzoai/cloud", path: "audit_middleware.go",
symbol: "scrubPath" }], verified: [{ method: "read", at: [{ repo: "hanzoai/cloud", path: "audit/re\
dact.go", symbol: "Redact" }, { repo: "hanzoai/cloud", path: "audit_middleware.go", symbol: "scrubPa\
th" }], detail: "The denylist covers password, secret, token, client secret and private key among ot\
hers, and the walk is recursive. The middleware never reads a request body, so there is no captured \
body to mask." }], maps: [{ clause: "soc2:CC6.1", strength: "partial" }, { clause: "iso27001:A.8.11",
strength: "partial" }, { clause: "iso27001:A.8.12", strength: "partial" }, { clause: "nist80053:AU",
strength: "partial" }, { clause: "nist80053:SC", strength: "partial" }] }, { id: "audit.reserved",
category: "corporate", claim: "Rows recording the platform's accountable actions are reserved: the\
API refuses to create, correct or delete one, so evidence cannot be forged with an invented grant o\
r quietly trimmed of a real one.", mechanism: "One predicate names the reserved actions, and the aud\
it surface calls it on every write verb \u2014 on create, on delete, and on update twice, for the stored \
row's action and for the incoming one, so a row cannot be forged by relabelling one you own.", status: "\
automated", enforced: [{ repo: "hanzoai/iam", path: "pkg/schema/audit_log.go", symbol: "PlatformWrit\
ten" }, { repo: "hanzoai/iam", path: "internal/auditlogs/auditlogs.go", symbol: "refusePlatformActio\
n" }, { repo: "hanzoai/cloud", path: "apps/admin/audit/audit.go", symbol: "Route" }, { repo: "hanzoa\
i/iam", path: "internal/auditlogs/reserved_test.go" }], verified: [{ method: "read", at: [{ repo: "h\
anzoai/iam", path: "pkg/schema/audit_log.go", symbol: "PlatformWritten" }, { repo: "hanzoai/iam", path: "\
internal/auditlogs/auditlogs.go", symbol: "refusePlatformAction" }, { repo: "hanzoai/cloud", path: "\
apps/admin/audit/audit.go", symbol: "Route" }, { repo: "hanzoai/iam", path: "internal/auditlogs/rese\
rved_test.go" }], detail: "A test at internal/auditlogs/reserved_test.go asserts this. The test seed\
s a reserved row the way the platform writes one, then attempts to create, update, relabel and delet\
e it through the API, and asserts all four are refused. Remove the gate and the test goes red. The c\
loud trail is stronger still: it publishes no write route at all, only a list and a chain check. Row\
s under consent-training, issue-user-token, mint-user-keys, revoke-user-keys, token-exchange evidenc\
e this. The five reserved actions. A row carrying one of these is evidence precisely because the sub\
ject of the evidence cannot write it." }, { method: "audit", actions: ["consent-training", "issue-us\
er-token", "mint-user-keys", "revoke-user-keys", "token-exchange"] }], maps: [{ clause: "soc2:CC7.2",
strength: "full" }, { clause: "soc2:CC4.1", strength: "full" }, { clause: "iso27001:A.5.28", strength: "\
full" }, { clause: "iso27001:A.8.15", strength: "full" }, { clause: "nist80053:AU", strength: "full" }] },
{ id: "audit.index", category: "infrastructure", claim: "Organization, user, action and time each \
carry an index, so a reviewer's question is a lookup rather than a walk through the whole trail.", mechanism: "\
The cloud audit table declares real indexes on organization with sequence, action with sequence, res\
ult with sequence, and timestamp.", status: "partial", note: "Partial on two counts, both measured r\
ather than reasoned. First, the cloud trail indexes organization, action and time but NOT user \u2014 so \
of the four questions named, 'everything one person did' is the one that still walks the table. Seco\
nd, the identity service's record declares index tags on organization, user, action and time and the\
y are INERT: the tag parser reads only two directives and index is not one of them, and that backend\
stores every entity as a JSON payload in a single shared table with indexes on kind, parent and del\
eted alone. The declaration exists; on this backend it materializes nothing.", enforced: [{ repo: "h\
anzoai/cloud", path: "audit/store.go", symbol: "schema" }, { repo: "hanzoai/orm", path: "model.go", symbol: "\
parseStructTags" }], verified: [{ method: "read", at: [{ repo: "hanzoai/cloud", path: "audit/store.g\
o", symbol: "schema" }, { repo: "hanzoai/orm", path: "model.go", symbol: "parseStructTags" }], detail: "\
The index statements in the cloud store were read directly. The inert-tag finding was measured, not \
inferred: an entity was written through the record layer to a real database file and the resulting s\
chema read back, which showed one shared table with a JSON payload column and three indexes, none of\
them on the declared fields." }], maps: [{ clause: "soc2:CC7.2", strength: "partial" }, { clause: "\
iso27001:A.8.15", strength: "partial" }, { clause: "iso27001:A.8.16", strength: "partial" }, { clause: "\
nist80053:AU", strength: "partial" }] }, { id: "audit.chain", category: "infrastructure", claim: "Ea\
ch record carries the previous record's hash and its own, so a row removed or altered after the fact\
is detectable rather than silent.", mechanism: "Records are appended over a monotonic sequence, eac\
h carrying the prior hash, and an endpoint walks the chain and reports where it breaks. The store is\
sues no update and no delete.", status: "automated", enforced: [{ repo: "hanzoai/cloud", path: "audi\
t/record.go", symbol: "Hash" }, { repo: "hanzoai/cloud", path: "apps/admin/audit/audit.go", symbol: "\
Verify" }, { repo: "hanzoai/cloud", path: "audit/store.go" }], verified: [{ method: "read", at: [{ repo: "\
hanzoai/cloud", path: "audit/record.go", symbol: "Hash" }, { repo: "hanzoai/cloud", path: "apps/admi\
n/audit/audit.go", symbol: "Verify" }, { repo: "hanzoai/cloud", path: "audit/store.go" }], detail: "\
Every record carries a previous hash and its own, the sequence is the primary key, and the store pac\
kage issues no update and no delete statement anywhere." }], maps: [{ clause: "soc2:CC7.2", strength: "\
full" }, { clause: "soc2:PI1.5", strength: "full" }, { clause: "iso27001:A.5.28", strength: "full" },
{ clause: "iso27001:A.8.15", strength: "full" }, { clause: "nist80053:AU", strength: "full" }, { clause: "\
nist80053:SI", strength: "full" }] }, { id: "privacy.consent", category: "data", claim: "A member's \
training-consent answer is recorded in the trail as a row the organization cannot author, alter or r\
emove.", mechanism: "The consent endpoint writes a row carrying the before and after state under a r\
eserved action, inside the same transaction as the change itself, so the record and the change land \
together or not at all.", status: "automated", enforced: [{ repo: "hanzoai/iam", path: "internal/oid\
c/consent.go", symbol: "auditConsent" }, { repo: "hanzoai/iam", path: "pkg/schema/audit_log.go", symbol: "\
ActionConsentTraining" }], verified: [{ method: "read", at: [{ repo: "hanzoai/iam", path: "internal/\
oidc/consent.go", symbol: "auditConsent" }, { repo: "hanzoai/iam", path: "pkg/schema/audit_log.go", symbol: "\
ActionConsentTraining" }], detail: "Rows under consent-training evidence this. One row per transitio\
n, carrying the from and to state. The action is reserved, which is what makes the row evidence rath\
er than an assertion." }, { method: "audit", actions: ["consent-training"] }], maps: [{ clause: "soc\
2:P2.1", strength: "full" }, { clause: "soc2:P6.2", strength: "full" }, { clause: "iso27001:A.5.34",
strength: "full" }, { clause: "nist80053:PT", strength: "full" }, { clause: "nist80053:AU", strength: "\
full" }] }, { id: "kms.signing.handle", category: "data", claim: "The signing interface takes a key \
id and some bytes and returns a signature. The private key is made inside an external module and sta\
ys there, so there is no moment when it exists in our process to be logged, leaked or written into a\
crash dump.", mechanism: "None found. Signing keys are held as strings on the identity service's ow\
n records and parsed in the running binary to build a signer. The cloud key service is embedded in t\
he binary, with a master key read from the environment.", status: "absent", note: "Recorded absent a\
fter searching the four repositories that serve this platform for any external key-service client. T\
here is none, and the key service exposes no signing function at all. Private keys are held and used\
in-process today. Threshold signing is wired but fails closed unless a backend is configured, so it\
is not a path in service. A Hanzo page states this control in the present tense and names four exte\
rnal modules; on the evidence available it is a roadmap item. This is the most consequential gap in \
this inventory.", enforced: [{ repo: "hanzoai/iam", path: "pkg/schema/cert.go", symbol: "PrivateKey" },
{ repo: "hanzoai/cloud", path: "clients/kms/kms.go", symbol: "New" }, { repo: "hanzoai/iam", path: "\
internal/oidc/jwt.go" }], verified: [{ method: "read", at: [{ repo: "hanzoai/iam", path: "pkg/schema\
/cert.go", symbol: "PrivateKey" }, { repo: "hanzoai/cloud", path: "clients/kms/kms.go", symbol: "New" },
{ repo: "hanzoai/iam", path: "internal/oidc/jwt.go" }], detail: "The private key is a string field\
on a record, and the signer is built by parsing it in the running binary. The one hardware-module m\
ention in the tree is a note explaining why a dependency was NOT taken." }], maps: [{ clause: "soc2:\
CC6.1", strength: "partial" }, { clause: "soc2:CC6.7", strength: "partial" }, { clause: "iso27001:A.\
8.24", strength: "partial" }, { clause: "nist80053:SC", strength: "partial" }] }, { id: "crypto.vali\
dation", category: "data", claim: "The cryptographic modules this platform runs hold an independent \
validation.", mechanism: "None. The code implements published standards, the lattice-based key-encap\
sulation and signature schemes among them. Implementing a standard is not the same as holding a vali\
dation, and a validation belongs to the vendor of a module rather than to the software that calls it\
.", status: "absent", note: "Carried so the gap is counted rather than omitted. There is nothing to \
read here \u2014 no certificate exists to point at \u2014 so this entry rests on a reading of what is absent r\
ather than on anything that could fail. Where a deployment needs validated modules, that is a questi\
on about which module signs, and it is answered by kms.signing.handle, which is itself absent.", enforced: [
{ repo: "hanzoai/iam", path: "internal/oidc/mldsa.go", symbol: "init" }], verified: [{ method: "re\
ad", at: [{ repo: "hanzoai/iam", path: "internal/oidc/mldsa.go", symbol: "init" }], detail: "The sig\
nature scheme is implemented in software inside our own binary. No external validated module is reac\
hed." }], maps: [{ clause: "iso27001:A.8.24", strength: "partial" }, { clause: "nist80053:SC", strength: "\
partial" }] }, { id: "ci.claim.copy", category: "product", claim: "No page we publish can say we hol\
d a credential an outside party would have to grant.", mechanism: "A gate reads every tracked page a\
nd refuses a line that names a published standard beside a word asserting we hold something under it\
, and refuses a hedge about a future assessment on the same line. It runs before anything is built, \
so a page that says it never reaches a reader.", status: "partial", note: "Partial for scope, not fo\
r quality. The gate itself refuses on the spot and the run stops. What is unestablished is its reach\
: it runs wherever a repository calls the shared pipeline, and the sweep that would say which reposi\
tories ought to and do not has not been done, so the honest word is the narrower one.", enforced: [{
repo: "hanzoai/ci", path: "bin/certclaims" }, { repo: "hanzoai/ci", path: ".hanzo/workflows/build.\
yml", note: "the step that runs the gates, ahead of every build and publish" }], verified: [{ method: "\
test", at: [{ repo: "hanzoai/ci", path: "bin/certclaims" }, { repo: "hanzoai/ci", path: ".hanzo/work\
flows/build.yml", note: "the step that runs the gates, ahead of every build and publish" }], detail: "\
The gate exits non-zero on a hit and the step runs under `set -e`, so a page making the claim fails \
the run rather than annotating it. bin/certclaims_test.sh drives it both ways." }], maps: [{ clause: "\
soc2:CC2.3", strength: "partial" }, { clause: "soc2:CC5.3", strength: "partial" }, { clause: "iso270\
01:A.5.36", strength: "partial" }, { clause: "nist80053:CM", strength: "partial" }] }, { id: "ci.mer\
ge.unresolved", category: "product", claim: "A merge nobody finished cannot ship.", mechanism: "A ga\
te refuses a tracked file still carrying the three-part marker git writes when it cannot merge. Thos\
e markers are ordinary content afterwards \u2014 the tree is clean, the diff is empty, and both sides of \
the merge ship \u2014 and in the config and data formats we publish they are silently valid.", status: "\
partial", note: "Partial for scope, not for quality. The gate itself refuses on the spot and the run\
stops. What is unestablished is its reach: it runs wherever a repository calls the shared pipeline,\
and the sweep that would say which repositories ought to and do not has not been done, so the hones\
t word is the narrower one.", enforced: [{ repo: "hanzoai/ci", path: "bin/conflictmarkers" }, { repo: "\
hanzoai/ci", path: ".hanzo/workflows/build.yml", note: "the step that runs the gates, ahead of every\
build and publish" }], verified: [{ method: "test", at: [{ repo: "hanzoai/ci", path: "bin/conflictm\
arkers" }, { repo: "hanzoai/ci", path: ".hanzo/workflows/build.yml", note: "the step that runs the g\
ates, ahead of every build and publish" }], detail: "Exits non-zero naming each file and line. It re\
quires the whole three-part shape and strips fenced regions in prose files, so a documented example \
of the format is not read as an unfinished merge." }], maps: [{ clause: "soc2:CC8.1", strength: "par\
tial" }, { clause: "iso27001:A.8.28", strength: "partial" }, { clause: "iso27001:A.8.32", strength: "\
partial" }, { clause: "nist80053:CM", strength: "partial" }, { clause: "nist80053:SI", strength: "pa\
rtial" }] }, { id: "ci.arg.public", category: "product", claim: "A value baked into a published imag\
e has to have said it was public first.", mechanism: "A build argument ends up inside the image, whe\
re anyone who can pull it can read it back. So the gate reads the names a repository declares and re\
fuses any that has not declared itself public by its own prefix. The name carries the assertion beca\
use at that moment the value does not exist yet, and guessing secrecy from the shape of a string is \
wrong in both directions.", status: "partial", note: "Partial for scope, not for quality. The gate i\
tself refuses on the spot and the run stops. What is unestablished is its reach: it runs wherever a \
repository calls the shared pipeline, and the sweep that would say which repositories ought to and d\
o not has not been done, so the honest word is the narrower one.", enforced: [{ repo: "hanzoai/ci", path: "\
bin/publishable" }, { repo: "hanzoai/ci", path: ".hanzo/workflows/build.yml", note: "the step that r\
uns the gates, ahead of every build and publish" }], verified: [{ method: "test", at: [{ repo: "hanz\
oai/ci", path: "bin/publishable" }, { repo: "hanzoai/ci", path: ".hanzo/workflows/build.yml", note: "\
the step that runs the gates, ahead of every build and publish" }], detail: "Exits non-zero on an un\
declared name and the image is never built. bin/publishable_test.sh drives the accepted and refused \
sets." }], maps: [{ clause: "soc2:CC6.1", strength: "partial" }, { clause: "soc2:CC6.7", strength: "\
partial" }, { clause: "iso27001:A.8.12", strength: "partial" }, { clause: "iso27001:A.8.25", strength: "\
partial" }, { clause: "nist80053:SA", strength: "partial" }, { clause: "nist80053:SC", strength: "pa\
rtial" }] }, { id: "surface.party", category: "product", claim: "A third party our pages reach is a \
third party this trust centre names.", mechanism: "A gate loads each surface as a browser would, tak\
es the hosts the markup names as subresources, reads the enforced content policy, and treats a host \
as reached only when the markup names it and the policy would permit it. A reached host that no part\
y in the subprocessor list declares fails the run, and so does any advertising or product-analytics \
host, which is matched over the whole document because the ordinary way one arrives is a script that\
builds another script at run time.", status: "partial", note: "Partial for scope. The rule is exact\
and it fails the run, but it measures the surfaces it is given, and those are named where it is inv\
oked rather than derived from the set of hosts the estate declares. It also sees only what the first\
response contains: a party a page reaches later, from code the browser runs, is outside what markup\
and a policy can show.", enforced: [{ repo: "hanzoai/trust", path: "bin/surface.mjs", symbol: "main" },
{ repo: "hanzoai/trust", path: "subprocessors.json", note: "the declared parties, and the origins \
each one is reached at" }], verified: [{ method: "test", at: [{ repo: "hanzoai/trust", path: "bin/su\
rface.mjs", symbol: "main" }, { repo: "hanzoai/trust", path: "hanzo.yml", note: "the surfaces it is \
run against" }], detail: "Exits non-zero naming the surface and the host. Run over the public surfac\
es it reports, per surface, whether a content policy is enforced at all and which hosts that policy \
refuses." }], maps: [{ clause: "soc2:CC3.4", strength: "partial" }, { clause: "soc2:CC9.2", strength: "\
partial" }, { clause: "iso27001:A.5.19", strength: "partial" }, { clause: "iso27001:A.5.22", strength: "\
partial" }, { clause: "nist80053:SR", strength: "partial" }] }, { id: "status.probe", category: "inc\
ident", claim: "Whether the platform is answering is measured continuously and published without a c\
redential.", mechanism: "A prober loops over each declared endpoint in its own goroutine, evaluates \
the conditions written for it, and keeps the transitions between healthy and unhealthy as events. Th\
e read side answers with no credential at all, so the record a customer reads is the record we read.",
status: "partial", note: "Partial for coverage. The prober watches the endpoints named in its conf\
iguration and the estate declares considerably more hosts than that, so a host nobody added is a hos\
t nothing watches. Uptime is also kept at hourly resolution for about two days and daily for thirty,\
so a question about a longer period has no answer here.", enforced: [{ repo: "hanzoai/status", path: "\
watchdog/watchdog.go", symbol: "Monitor" }, { repo: "hanzoai/status", path: "api/endpoint_status.go",
symbol: "EndpointStatuses" }, { repo: "hanzoai/status", path: "api/raw.go", symbol: "UptimeRaw" }],
verified: [{ method: "read", at: [{ repo: "hanzoai/status", path: "watchdog/watchdog.go", symbol: "\
Monitor" }, { repo: "hanzoai/status", path: "api/endpoint_status.go", symbol: "EndpointStatuses" }],
detail: "The prober loop and the two read paths were read in the source, and the public read was e\
xercised against the running service. Nothing here can fail on the control's behalf, so it is a read\
ing and counts as one." }], maps: [{ clause: "soc2:CC2.3", strength: "partial" }, { clause: "soc2:CC\
7.2", strength: "partial" }, { clause: "iso27001:A.8.16", strength: "partial" }, { clause: "nist8005\
3:SI", strength: "partial" }] }, { id: "incident.notice", category: "incident", claim: "A failing pr\
obe reaches a person, and a reader is told what happened.", mechanism: "The prober's alerting path f\
ires on a run of failures and resolves on recovery, addressed to the operations channel. What a visi\
tor reads is a separate thing: a notice carries a type and a message and is published by editing the\
service's own configuration.", status: "partial", note: "The half that wakes somebody is automatic;\
the half a customer reads is not. A notice is a hand-written entry, so nothing derives one from a f\
ailing probe and nothing requires that one be written. The public record can therefore be silent thr\
ough an event the alerting path reported, and only a person closes that gap.", enforced: [{ repo: "h\
anzoai/status", path: "watchdog/alerting.go" }, { repo: "hanzoai/status", path: "config/announcement\
/announcement.go" }], verified: [{ method: "read", at: [{ repo: "hanzoai/status", path: "watchdog/al\
erting.go" }, { repo: "hanzoai/status", path: "config/announcement/announcement.go" }], detail: "Bot\
h paths were read. The notice type is a closed set and the list is served on the public configuratio\
n read, so a published notice is reachable without a credential once it exists." }], maps: [{ clause: "\
soc2:CC7.3", strength: "partial" }, { clause: "soc2:CC7.4", strength: "partial" }, { clause: "iso270\
01:A.5.24", strength: "partial" }, { clause: "iso27001:A.5.26", strength: "partial" }, { clause: "is\
o27001:A.6.8", strength: "partial" }, { clause: "nist80053:IR", strength: "partial" }] }, { id: "end\
point.posture", category: "endpoint", claim: "The state of the machine a person signs in from is tak\
en into account.", mechanism: "None. Signing in proves a password and then a second factor, and both\
are facts about the person rather than about the device in front of them. Nothing in the flow reads\
a device identity, a disk-encryption state, a patch level or a management enrolment, and there is n\
o register of machines against which such a signal could be resolved.", status: "absent", note: "Car\
ried so the group is answered rather than left blank. The place named below is where the signal woul\
d be read \u2014 the point after the password is accepted and before a session exists \u2014 and reading it is\
how you can see that nothing consults one. This is the whole of what we assert about the devices pe\
ople work from.", enforced: [{ repo: "hanzoai/iam", path: "internal/oidc/mfa_gate.go", symbol: "Gate",
note: "the second-factor gate \u2014 the point in the flow a device signal would be read at" }], verified: [
{ method: "read", at: [{ repo: "hanzoai/iam", path: "internal/oidc/mfa_gate.go", symbol: "Gate" }],
detail: "The gate was read end to end. It resolves the factors a user and their organization allow\
and proves one of them; no branch of it reads anything about the machine the request came from." }],
maps: [{ clause: "soc2:CC6.1", strength: "partial" }, { clause: "iso27001:A.6.7", strength: "parti\
al" }, { clause: "iso27001:A.8.1", strength: "partial" }, { clause: "nist80053:AC", strength: "parti\
al" }] }, { id: "edge.transport", category: "network", claim: "A browser is told to reach every one \
of our hosts over TLS and nothing else.", mechanism: "One shared piece of edge configuration sets th\
e transport-security header for a year with subdomains included, and carries the frame, sniffing, re\
ferrer and permissions headers with it. It is attached per route rather than at the entry point, so \
a route carries it when whoever added the route said so.", status: "partial", note: "Measured rather\
than assumed, and the measurement is why this is not automated. Fewer than half the declared routes\
name the shared configuration, so most hosts assert nothing; and on the wire the header is absent f\
rom public hosts whose route does name it, so naming it is not sufficient either. Setting it once at\
the entry point would make the default right and make a route that opts out visible, which is the o\
pposite of the arrangement today.", enforced: [{ repo: "hanzoai/universe", path: "infra/k8s/ingress/\
routes.yaml", note: "the shared header configuration, and the routes that name it" }], verified: [{ method: "\
read", at: [{ repo: "hanzoai/universe", path: "infra/k8s/ingress/routes.yaml" }], detail: "The routi\
ng table was parsed and the routes naming the shared header configuration were counted against the w\
hole set, and the header was then read off the wire from several public hosts. Nothing asserts this \
on our behalf between one reading and the next, so it is a reading." }], maps: [{ clause: "soc2:CC6.\
6", strength: "partial" }, { clause: "soc2:CC6.7", strength: "partial" }, { clause: "iso27001:A.8.20",
strength: "partial" }, { clause: "iso27001:A.8.21", strength: "partial" }, { clause: "iso27001:A.8\
.24", strength: "partial" }, { clause: "nist80053:SC", strength: "partial" }] }, { id: "dataroom.tie\
r", category: "data", claim: "An artifact somebody outside this organization signed can never be pub\
lished to everyone.", mechanism: "The storage engine refuses it. The table holding a published artif\
act carries a constraint saying an item whose signer is external must stay gated, and the tier colum\
n defaults to gated \u2014 so a kind nobody has thought of yet arrives private and somebody has to open i\
t deliberately.", status: "automated", enforced: [{ repo: "hanzoai/cloud", path: "apps/dataroom/sche\
ma.go", note: "the artifact table and the three constraints on it" }], verified: [{ method: "test", at: [
{ repo: "hanzoai/cloud", path: "apps/dataroom/trust_test.go", symbol: "TestAuditorSignedCannotBePu\
blic" }, { repo: "hanzoai/cloud", path: "apps/dataroom/trust_test.go", symbol: "TestTierDefaultsToGa\
ted" }, { repo: "hanzoai/cloud", path: "apps/dataroom/trust_test.go", symbol: "TestPublicCenterWithh\
oldsTheGatedTier" }], detail: "Three cases drive it: the write is refused, the default holds, and th\
e public reading of a centre carries no gated item. Because the rule lives in the schema, a path thr\
ough the code written later cannot get round it \u2014 which is what these assert, rather than the behavi\
our of any one handler." }], maps: [{ clause: "soc2:C1.1", strength: "full" }, { clause: "soc2:CC6.1",
strength: "partial" }, { clause: "iso27001:A.5.12", strength: "partial" }, { clause: "iso27001:A.8\
.3", strength: "full" }, { clause: "nist80053:AC", strength: "partial" }] }, { id: "dataroom.grant",
category: "access", claim: "A gated artifact opens for the party that asked for it and for nobody \
else.", mechanism: "A request to read the gated tier is a row with a state. Granting it produces a l\
ink bound to the party that asked, so the queue and the live grants are one list, and an administrat\
or of another organization reaches none of it.", status: "automated", enforced: [{ repo: "hanzoai/cl\
oud", path: "apps/dataroom/trust.go" }, { repo: "hanzoai/cloud", path: "apps/dataroom/schema.go", note: "\
the request table and the states it may hold" }], verified: [{ method: "test", at: [{ repo: "hanzoai\
/cloud", path: "apps/dataroom/trust_test.go", symbol: "TestGrantOpensOnlyForThePartyThatAsked" }, { repo: "\
hanzoai/cloud", path: "apps/dataroom/trust_test.go", symbol: "TestAForeignOrgAdminReachesNothing" },
{ repo: "hanzoai/cloud", path: "apps/dataroom/trust_test.go", symbol: "TestAskIsRecordedOrRefused" }],
detail: "Driven from both sides: the party that asked gets in, a second party holding the same add\
ress does not, and an administrator of another organization reaches nothing at all." }], maps: [{ clause: "\
soc2:CC6.1", strength: "full" }, { clause: "soc2:CC6.3", strength: "full" }, { clause: "soc2:C1.1", strength: "\
partial" }, { clause: "iso27001:A.5.15", strength: "full" }, { clause: "iso27001:A.8.3", strength: "\
full" }, { clause: "nist80053:AC", strength: "partial" }] }, { id: "dataroom.trail", category: "data",
claim: "Reading a released document leaves a record of who read which page.", mechanism: "A viewin\
g session records a page-view at a time against the link it was opened with, so what a reviewer actu\
ally opened is a list rather than a recollection.", status: "partial", note: "Partial because of wha\
t the record can and cannot prove. The session and the pages are ours and are written on our side, b\
ut the address naming the viewer is taken as stated and is not proven \u2014 so the trail establishes tha\
t a link was used and which pages were reached, and does not establish who was holding it.", enforced: [
{ repo: "hanzoai/cloud", path: "apps/dataroom/dataroom.go", note: "the page-view record on an open\
viewing session" }], verified: [{ method: "read", at: [{ repo: "hanzoai/cloud", path: "apps/dataroo\
m/dataroom.go" }], detail: "The route and the record it writes were read, along with the note record\
ing that the viewer address is taken as stated. Nothing here fails on the control's behalf." }], maps: [
{ clause: "soc2:CC6.8", strength: "partial" }, { clause: "soc2:P6.2", strength: "partial" }, { clause: "\
iso27001:A.8.15", strength: "partial" }, { clause: "nist80053:AU", strength: "partial" }] }];
// <define:__FRAMEWORKS__>
var define_FRAMEWORKS_default = { $comment: "Framework clause catalogs. Reference data: the publis\
hed clause lists, verbatim in id and title, with no Hanzo judgement in them. They are the DENOMINATO\
R of every coverage number \u2014 a control in controls.json may only map to an id that appears here, whi\
ch is what makes a typo a build failure instead of a silently uncounted clause. Every field here des\
cribes a published clause, so coverage against that list is the only thing this file can say.", soc2: {
name: "SOC 2 Trust Services Criteria", edition: "2017, with the 2022 revised points of focus", publisher: "\
AICPA", unit: "criterion", units: "criteria", note: "All five trust services categories. Security (t\
he common criteria) applies to every engagement; the other four are included only when in scope for \
a given report.", clauses: [{ id: "CC1.1", group: "Control Environment", title: "Demonstrates a comm\
itment to integrity and ethical values" }, { id: "CC1.2", group: "Control Environment", title: "Boar\
d of directors demonstrates independence and exercises oversight" }, { id: "CC1.3", group: "Control \
Environment", title: "Establishes structures, reporting lines, authorities and responsibilities" }, {
id: "CC1.4", group: "Control Environment", title: "Demonstrates a commitment to attract, develop a\
nd retain competent individuals" }, { id: "CC1.5", group: "Control Environment", title: "Holds indiv\
iduals accountable for their internal control responsibilities" }, { id: "CC2.1", group: "Communicat\
ion and Information", title: "Obtains or generates relevant, quality information to support internal\
control" }, { id: "CC2.2", group: "Communication and Information", title: "Internally communicates \
information necessary to support internal control" }, { id: "CC2.3", group: "Communication and Infor\
mation", title: "Communicates with external parties regarding matters affecting internal control" },
{ id: "CC3.1", group: "Risk Assessment", title: "Specifies objectives with sufficient clarity to i\
dentify and assess risk" }, { id: "CC3.2", group: "Risk Assessment", title: "Identifies and analyzes\
risk to the achievement of its objectives" }, { id: "CC3.3", group: "Risk Assessment", title: "Cons\
iders the potential for fraud in assessing risk" }, { id: "CC3.4", group: "Risk Assessment", title: "\
Identifies and assesses changes that could significantly impact internal control" }, { id: "CC4.1", group: "\
Monitoring Activities", title: "Selects, develops and performs ongoing and separate evaluations of c\
ontrols" }, { id: "CC4.2", group: "Monitoring Activities", title: "Evaluates and communicates intern\
al control deficiencies in a timely manner" }, { id: "CC5.1", group: "Control Activities", title: "S\
elects and develops control activities that mitigate risk to acceptable levels" }, { id: "CC5.2", group: "\
Control Activities", title: "Selects and develops general control activities over technology" }, { id: "\
CC5.3", group: "Control Activities", title: "Deploys control activities through policies and procedu\
res" }, { id: "CC6.1", group: "Logical and Physical Access", title: "Implements logical access secur\
ity software, infrastructure and architectures over protected information assets" }, { id: "CC6.2", group: "\
Logical and Physical Access", title: "Registers and authorizes new internal and external users befor\
e issuing credentials" }, { id: "CC6.3", group: "Logical and Physical Access", title: "Authorizes, m\
odifies or removes access based on roles, responsibilities and least privilege" }, { id: "CC6.4", group: "\
Logical and Physical Access", title: "Restricts physical access to facilities and protected informat\
ion assets" }, { id: "CC6.5", group: "Logical and Physical Access", title: "Discontinues logical and\
physical protections over assets on disposal" }, { id: "CC6.6", group: "Logical and Physical Access",
title: "Implements logical access security measures against threats from outside the system bounda\
ries" }, { id: "CC6.7", group: "Logical and Physical Access", title: "Restricts the transmission, mo\
vement and removal of information" }, { id: "CC6.8", group: "Logical and Physical Access", title: "I\
mplements controls to prevent or detect and act upon unauthorized or malicious software" }, { id: "C\
C7.1", group: "System Operations", title: "Uses detection and monitoring procedures to identify conf\
iguration changes and vulnerabilities" }, { id: "CC7.2", group: "System Operations", title: "Monitor\
s system components for anomalies indicative of malicious acts, natural disasters and errors" }, { id: "\
CC7.3", group: "System Operations", title: "Evaluates security events to determine whether they coul\
d or have resulted in a failure" }, { id: "CC7.4", group: "System Operations", title: "Responds to i\
dentified security incidents" }, { id: "CC7.5", group: "System Operations", title: "Identifies, deve\
lops and implements activities to recover from identified security incidents" }, { id: "CC8.1", group: "\
Change Management", title: "Authorizes, designs, develops, tests, approves and implements changes to\
infrastructure, data, software and procedures" }, { id: "CC9.1", group: "Risk Mitigation", title: "\
Identifies, selects and develops risk mitigation activities for business disruptions" }, { id: "CC9.\
2", group: "Risk Mitigation", title: "Assesses and manages risks associated with vendors and busines\
s partners" }, { id: "A1.1", group: "Availability", title: "Maintains, monitors and evaluates curren\
t processing capacity and use of system components" }, { id: "A1.2", group: "Availability", title: "\
Authorizes, designs, implements and maintains environmental protections, backup and recovery infrast\
ructure" }, { id: "A1.3", group: "Availability", title: "Tests recovery plan procedures supporting s\
ystem recovery" }, { id: "C1.1", group: "Confidentiality", title: "Identifies and maintains confiden\
tial information to meet confidentiality objectives" }, { id: "C1.2", group: "Confidentiality", title: "\
Disposes of confidential information to meet confidentiality objectives" }, { id: "PI1.1", group: "P\
rocessing Integrity", title: "Obtains, generates, uses and communicates relevant quality information\
about processing objectives" }, { id: "PI1.2", group: "Processing Integrity", title: "Implements po\
licies and procedures over system inputs" }, { id: "PI1.3", group: "Processing Integrity", title: "I\
mplements policies and procedures over system processing" }, { id: "PI1.4", group: "Processing Integ\
rity", title: "Implements policies and procedures to make output available in accordance with specif\
ications" }, { id: "PI1.5", group: "Processing Integrity", title: "Implements policies and procedure\
s to store inputs, items in processing and outputs completely and accurately" }, { id: "P1.1", group: "\
Privacy \u2014 Notice", title: "Provides notice about its privacy practices to data subjects" }, { id: "\
P2.1", group: "Privacy \u2014 Choice and Consent", title: "Communicates choices available regarding \
personal information and obtains consent" }, { id: "P3.1", group: "Privacy \u2014 Collection", title: "\
Collects personal information consistent with its objectives" }, { id: "P3.2", group: "Privacy \u2014 Col\
lection", title: "Obtains explicit consent for sensitive personal information" }, { id: "P4.1", group: "\
Privacy \u2014 Use, Retention and Disposal", title: "Limits the use of personal information to its s\
tated purposes" }, { id: "P4.2", group: "Privacy \u2014 Use, Retention and Disposal", title: "Retain\
s personal information consistent with its objectives" }, { id: "P4.3", group: "Privacy \u2014 Use, Reten\
tion and Disposal", title: "Securely disposes of personal information" }, { id: "P5.1", group: "Priv\
acy \u2014 Access", title: "Grants identified and authenticated data subjects access to their person\
al information" }, { id: "P5.2", group: "Privacy \u2014 Access", title: "Corrects, amends or appends\
personal information on request" }, { id: "P6.1", group: "Privacy \u2014 Disclosure and Notification",
title: "Discloses personal information only with the consent of the data subject" }, { id: "P6.2",
group: "Privacy \u2014 Disclosure and Notification", title: "Creates and retains a record of autho\
rized disclosures" }, { id: "P6.3", group: "Privacy \u2014 Disclosure and Notification", title: "Cre\
ates and retains a record of detected or reported unauthorized disclosures" }, { id: "P6.4", group: "\
Privacy \u2014 Disclosure and Notification", title: "Obtains privacy commitments from third parties \
to whom information is disclosed" }, { id: "P6.5", group: "Privacy \u2014 Disclosure and Notification",
title: "Obtains commitments from third parties to notify of unauthorized disclosures" }, { id: "P6\
.6", group: "Privacy \u2014 Disclosure and Notification", title: "Provides notification of breaches \
and incidents to affected data subjects, regulators and others" }, { id: "P6.7", group: "Privacy \u2014 D\
isclosure and Notification", title: "Provides data subjects with an accounting of personal informati\
on held and disclosures made" }, { id: "P7.1", group: "Privacy \u2014 Quality", title: "Collects and\
maintains accurate, complete and relevant personal information" }, { id: "P8.1", group: "Privacy \u2014 \
Monitoring and Enforcement", title: "Implements a process for receiving, addressing and resolving pr\
ivacy complaints" }] }, iso27001: { name: "ISO/IEC 27001 Annex A", edition: "2022", publisher: "ISO/\
IEC", unit: "control", units: "controls", note: "All 93 Annex A controls across the four themes. Ann\
ex A is a reference set; a real Statement of Applicability scopes it per organization. This catalog \
does not scope it \u2014 every control is counted, and the ones no engineering control can reach show as \
uncovered rather than as excluded.", clauses: [{ id: "A.5.1", group: "Organizational", title: "Polic\
ies for information security" }, { id: "A.5.2", group: "Organizational", title: "Information securit\
y roles and responsibilities" }, { id: "A.5.3", group: "Organizational", title: "Segregation of duti\
es" }, { id: "A.5.4", group: "Organizational", title: "Management responsibilities" }, { id: "A.5.5",
group: "Organizational", title: "Contact with authorities" }, { id: "A.5.6", group: "Organizationa\
l", title: "Contact with special interest groups" }, { id: "A.5.7", group: "Organizational", title: "\
Threat intelligence" }, { id: "A.5.8", group: "Organizational", title: "Information security in proj\
ect management" }, { id: "A.5.9", group: "Organizational", title: "Inventory of information and othe\
r associated assets" }, { id: "A.5.10", group: "Organizational", title: "Acceptable use of informati\
on and other associated assets" }, { id: "A.5.11", group: "Organizational", title: "Return of assets" },
{ id: "A.5.12", group: "Organizational", title: "Classification of information" }, { id: "A.5.13",
group: "Organizational", title: "Labelling of information" }, { id: "A.5.14", group: "Organization\
al", title: "Information transfer" }, { id: "A.5.15", group: "Organizational", title: "Access contro\
l" }, { id: "A.5.16", group: "Organizational", title: "Identity management" }, { id: "A.5.17", group: "\
Organizational", title: "Authentication information" }, { id: "A.5.18", group: "Organizational", title: "\
Access rights" }, { id: "A.5.19", group: "Organizational", title: "Information security in supplier \
relationships" }, { id: "A.5.20", group: "Organizational", title: "Addressing information security w\
ithin supplier agreements" }, { id: "A.5.21", group: "Organizational", title: "Managing information \
security in the ICT supply chain" }, { id: "A.5.22", group: "Organizational", title: "Monitoring, re\
view and change management of supplier services" }, { id: "A.5.23", group: "Organizational", title: "\
Information security for use of cloud services" }, { id: "A.5.24", group: "Organizational", title: "\
Information security incident management planning and preparation" }, { id: "A.5.25", group: "Organi\
zational", title: "Assessment and decision on information security events" }, { id: "A.5.26", group: "\
Organizational", title: "Response to information security incidents" }, { id: "A.5.27", group: "Orga\
nizational", title: "Learning from information security incidents" }, { id: "A.5.28", group: "Organi\
zational", title: "Collection of evidence" }, { id: "A.5.29", group: "Organizational", title: "Infor\
mation security during disruption" }, { id: "A.5.30", group: "Organizational", title: "ICT readiness\
for business continuity" }, { id: "A.5.31", group: "Organizational", title: "Legal, statutory, regu\
latory and contractual requirements" }, { id: "A.5.32", group: "Organizational", title: "Intellectua\
l property rights" }, { id: "A.5.33", group: "Organizational", title: "Protection of records" }, { id: "\
A.5.34", group: "Organizational", title: "Privacy and protection of personally identifiable informat\
ion" }, { id: "A.5.35", group: "Organizational", title: "Independent review of information security" },
{ id: "A.5.36", group: "Organizational", title: "Compliance with policies, rules and standards for\
information security" }, { id: "A.5.37", group: "Organizational", title: "Documented operating proc\
edures" }, { id: "A.6.1", group: "People", title: "Screening" }, { id: "A.6.2", group: "People", title: "\
Terms and conditions of employment" }, { id: "A.6.3", group: "People", title: "Information security \
awareness, education and training" }, { id: "A.6.4", group: "People", title: "Disciplinary process" },
{ id: "A.6.5", group: "People", title: "Responsibilities after termination or change of employment" },
{ id: "A.6.6", group: "People", title: "Confidentiality or non-disclosure agreements" }, { id: "A.\
6.7", group: "People", title: "Remote working" }, { id: "A.6.8", group: "People", title: "Informatio\
n security event reporting" }, { id: "A.7.1", group: "Physical", title: "Physical security perimeter\
s" }, { id: "A.7.2", group: "Physical", title: "Physical entry" }, { id: "A.7.3", group: "Physical",
title: "Securing offices, rooms and facilities" }, { id: "A.7.4", group: "Physical", title: "Physi\
cal security monitoring" }, { id: "A.7.5", group: "Physical", title: "Protecting against physical an\
d environmental threats" }, { id: "A.7.6", group: "Physical", title: "Working in secure areas" }, { id: "\
A.7.7", group: "Physical", title: "Clear desk and clear screen" }, { id: "A.7.8", group: "Physical",
title: "Equipment siting and protection" }, { id: "A.7.9", group: "Physical", title: "Security of \
assets off-premises" }, { id: "A.7.10", group: "Physical", title: "Storage media" }, { id: "A.7.11",
group: "Physical", title: "Supporting utilities" }, { id: "A.7.12", group: "Physical", title: "Cab\
ling security" }, { id: "A.7.13", group: "Physical", title: "Equipment maintenance" }, { id: "A.7.14",
group: "Physical", title: "Secure disposal or re-use of equipment" }, { id: "A.8.1", group: "Techn\
ological", title: "User endpoint devices" }, { id: "A.8.2", group: "Technological", title: "Privileg\
ed access rights" }, { id: "A.8.3", group: "Technological", title: "Information access restriction" },
{ id: "A.8.4", group: "Technological", title: "Access to source code" }, { id: "A.8.5", group: "Te\
chnological", title: "Secure authentication" }, { id: "A.8.6", group: "Technological", title: "Capac\
ity management" }, { id: "A.8.7", group: "Technological", title: "Protection against malware" }, { id: "\
A.8.8", group: "Technological", title: "Management of technical vulnerabilities" }, { id: "A.8.9", group: "\
Technological", title: "Configuration management" }, { id: "A.8.10", group: "Technological", title: "\
Information deletion" }, { id: "A.8.11", group: "Technological", title: "Data masking" }, { id: "A.8\
.12", group: "Technological", title: "Data leakage prevention" }, { id: "A.8.13", group: "Technologi\
cal", title: "Information backup" }, { id: "A.8.14", group: "Technological", title: "Redundancy of i\
nformation processing facilities" }, { id: "A.8.15", group: "Technological", title: "Logging" }, { id: "\
A.8.16", group: "Technological", title: "Monitoring activities" }, { id: "A.8.17", group: "Technolog\
ical", title: "Clock synchronization" }, { id: "A.8.18", group: "Technological", title: "Use of priv\
ileged utility programs" }, { id: "A.8.19", group: "Technological", title: "Installation of software\
on operational systems" }, { id: "A.8.20", group: "Technological", title: "Networks security" }, { id: "\
A.8.21", group: "Technological", title: "Security of network services" }, { id: "A.8.22", group: "Te\
chnological", title: "Segregation of networks" }, { id: "A.8.23", group: "Technological", title: "We\
b filtering" }, { id: "A.8.24", group: "Technological", title: "Use of cryptography" }, { id: "A.8.2\
5", group: "Technological", title: "Secure development life cycle" }, { id: "A.8.26", group: "Techno\
logical", title: "Application security requirements" }, { id: "A.8.27", group: "Technological", title: "\
Secure system architecture and engineering principles" }, { id: "A.8.28", group: "Technological", title: "\
Secure coding" }, { id: "A.8.29", group: "Technological", title: "Security testing in development an\
d acceptance" }, { id: "A.8.30", group: "Technological", title: "Outsourced development" }, { id: "A\
.8.31", group: "Technological", title: "Separation of development, test and production environments" },
{ id: "A.8.32", group: "Technological", title: "Change management" }, { id: "A.8.33", group: "Tech\
nological", title: "Test information" }, { id: "A.8.34", group: "Technological", title: "Protection \
of information systems during audit testing" }] }, nist80053: { name: "NIST SP 800-53 control famili\
es", edition: "Revision 5", publisher: "NIST", unit: "family", units: "families", note: "Mapped at F\
AMILY granularity, not to individual controls. Rev 5 has over a thousand controls and enhancements; \
claiming a per-control mapping we have not performed would be the exact dishonesty this system exist\
s to prevent. A family counts as automated when at least one verified automated control lands inside\
it \u2014 that is a weaker statement than covering the family, and it is stated that way everywhere it i\
s reported.", clauses: [{ id: "AC", group: "800-53 Rev 5", title: "Access Control" }, { id: "AT", group: "\
800-53 Rev 5", title: "Awareness and Training" }, { id: "AU", group: "800-53 Rev 5", title: "Audit a\
nd Accountability" }, { id: "CA", group: "800-53 Rev 5", title: "Assessment, Authorization and Monit\
oring" }, { id: "CM", group: "800-53 Rev 5", title: "Configuration Management" }, { id: "CP", group: "\
800-53 Rev 5", title: "Contingency Planning" }, { id: "IA", group: "800-53 Rev 5", title: "Identific\
ation and Authentication" }, { id: "IR", group: "800-53 Rev 5", title: "Incident Response" }, { id: "\
MA", group: "800-53 Rev 5", title: "Maintenance" }, { id: "MP", group: "800-53 Rev 5", title: "Media\
Protection" }, { id: "PE", group: "800-53 Rev 5", title: "Physical and Environmental Protection" },
{ id: "PL", group: "800-53 Rev 5", title: "Planning" }, { id: "PM", group: "800-53 Rev 5", title: "\
Program Management" }, { id: "PS", group: "800-53 Rev 5", title: "Personnel Security" }, { id: "PT",
group: "800-53 Rev 5", title: "Personally Identifiable Information Processing and Transparency" },
{ id: "RA", group: "800-53 Rev 5", title: "Risk Assessment" }, { id: "SA", group: "800-53 Rev 5", title: "\
System and Services Acquisition" }, { id: "SC", group: "800-53 Rev 5", title: "System and Communicat\
ions Protection" }, { id: "SI", group: "800-53 Rev 5", title: "System and Information Integrity" }, {
id: "SR", group: "800-53 Rev 5", title: "Supply Chain Risk Management" }] } };
// <define:__SUBPROCESSORS__>
var define_SUBPROCESSORS_default = [{ id: "digitalocean", name: "DigitalOcean", role: "processor",
mark: "digitalocean", purpose: "The infrastructure everything runs on: the Kubernetes clusters, th\
e block volumes each database lives on, and the load balancers that terminate traffic. Separately, t\
he inference endpoint that serves embeddings, images, video and reranking.", data: "Everything. Ever\
y request reaches a process on their compute, and every byte we store at rest sits on their volumes,\
so there is no customer record that is not on their hardware. The inference endpoint additionally r\
eceives the text or image sent to it.", location: "United States (sfo3), on the operator's global fo\
otprint", url: "https://www.digitalocean.com", terms: "https://www.digitalocean.com/legal/data-proce\
ssing-agreement", evidence: [{ repo: "hanzoai/universe", path: "infra/terraform/main.tf", note: "the\
provider, the network, the firewall and the nodes are declared here" }, { repo: "hanzoai/universe",
path: "infra/k8s/ingress/service.yaml", note: "the load balancer that terminates public traffic" },
{ repo: "hanzoai/universe", path: "charts/app/values/hanzo/cloud.yaml", note: "the inference endpo\
int and the key that reaches it" }] }, { id: "cloudflare", name: "Cloudflare", role: "processor", mark: "\
cloudflare", purpose: "Authoritative DNS for every zone, and the reverse proxy in front of the hosts\
marked proxied \u2014 which means they terminate TLS for those and see the request in the clear. A worke\
r sits in the analytics path, the sign-in pages are served from their hosting, and aggregate mail re\
ports are addressed to them.", data: "For a proxied host: the whole request. The visitor's address, \
the URL, the headers, the body, and the response. Their page-timing script is served to hanzo.ai and\
reports that visitor's page views back to them.", location: "United States, served from the operato\
r's global edge", url: "https://www.cloudflare.com", terms: "https://www.cloudflare.com/cloudflare-c\
ustomer-dpa/", evidence: [{ repo: "hanzoai/universe", path: "infra/cf-zones/hanzo-ai.yaml", note: "t\
he zone, the records marked proxied, and the address aggregate mail reports go to" }, { repo: "hanzo\
ai/universe", path: "workers/analytics-proxy/worker.js", note: "a worker in the request path of ever\
y analytics beacon" }, { repo: "hanzoai/universe", path: "charts/app/values/hanzo/tabs.yaml", note: "\
records that the page-timing script is inserted at the edge rather than by us, so it appears in no p\
age we build" }, { origin: "static.cloudflareinsights.com", note: "the page-timing script. Inserted \
into every hanzo.ai host; only the surfaces whose enforced policy omits it refuse to run it" }] }, {
id: "openrouter", name: "OpenRouter", role: "processor", mark: "openrouter", purpose: "Model infer\
ence. The route most text and vision requests take.", data: "The prompt as sent, including whatever \
a customer put in it, and the completion returned. They route onward to the operator that runs the m\
odel, so a prompt reaching them may reach that operator too.", location: "United States", url: "http\
s://openrouter.ai", terms: "https://openrouter.ai/terms", evidence: [{ repo: "hanzoai/universe", path: "\
charts/app/values/hanzo/cloud.yaml", note: "the endpoint and the key" }, { repo: "hanzoai/universe",
path: "charts/app/values/enso/enso.yaml", note: "the same endpoint declared for the router" }] }, {
id: "fireworks", name: "Fireworks AI", role: "processor", purpose: "The embedder behind the knowle\
dge store \u2014 it turns a document into the vector the store is searched by.", data: "The text of \
every document ingested into a knowledge store, and the text of every query run against one. This is\
the widest document flow on this list and it is worth reading twice: a file a customer uploads for \
retrieval is sent here in full.", location: "United States", url: "https://fireworks.ai", terms: "ht\
tps://fireworks.ai/terms-of-service", evidence: [{ repo: "hanzoai/universe", path: "charts/app/value\
s/hanzo/cloud.yaml", note: "the embedding endpoint, the model and the key reference" }] }, { id: "op\
enai", name: "OpenAI", role: "processor", purpose: "Model inference, on a key we hold.", data: "The \
prompt sent to a model they serve, and the completion returned.", location: "United States", url: "h\
ttps://openai.com", terms: "https://openai.com/policies/data-processing-addendum", evidence: [{ repo: "\
hanzoai/universe", path: "charts/app/values/hanzo/cloud.yaml", note: "the key, held in the shared mo\
del-key secret" }] }, { id: "anthropic", name: "Anthropic", role: "processor", mark: "anthropic", purpose: "\
Model inference, on a key we hold.", data: "The prompt sent to a model they serve, and the completio\
n returned.", location: "United States", url: "https://www.anthropic.com", terms: "https://www.anthr\
opic.com/legal/commercial-terms", evidence: [{ repo: "hanzoai/universe", path: "charts/app/values/ha\
nzo/cloud.yaml", note: "the key, held in the shared model-key secret" }] }, { id: "deepseek", name: "\
DeepSeek", role: "processor", mark: "deepseek", purpose: "Model inference, on a key we hold.", data: "\
The prompt sent to a model they serve, and the completion returned.", location: "China", url: "https\
://www.deepseek.com", terms: "https://platform.deepseek.com/downloads/DeepSeek%20Open%20Platform%20T\
erms%20of%20Service.html", evidence: [{ repo: "hanzoai/universe", path: "cloud/src/providers.rs", note: "\
the endpoint" }] }, { id: "twilio", name: "Twilio", role: "processor", purpose: "Message delivery \u2014 \
text, voice and chat through the messaging interface, and mail through the mail interface. One party\
, two products.", data: "The recipient's number or address, and the body of the message sent to them\
.", location: "United States", url: "https://www.twilio.com", terms: "https://www.twilio.com/en-us/l\
egal/data-protection-addendum", evidence: [{ repo: "hanzoai/cloud", path: "apps/notify/twilio.go", symbol: "\
register", note: "one party registered as two providers, and the credentials each reads" }, { repo: "\
hanzoai/universe", path: "charts/app/values/hanzo/iam.yaml", note: "records that the credentials are\
held per organization in the key store rather than as a deployment secret" }] }, { id: "google", name: "\
Google", role: "processor", mark: "google", purpose: "Two unrelated things. Signing in with a Google\
account, which is a customer's own choice at the sign-in page. And the mail exchange for our own do\
mains, which is where a message a customer sends us arrives.", data: "For sign-in: the account ident\
ifier and the profile fields that flow back with it. For mail: any message a customer addresses to u\
s, in full.", location: "United States", url: "https://workspace.google.com", terms: "https://cloud.\
google.com/terms/data-processing-addendum", evidence: [{ repo: "hanzoai/universe", path: "infra/k8s/\
kms-canonical-crs/hanzo/google-oauth-kms-sync.yaml", note: "the sign-in credentials" }, { repo: "han\
zoai/universe", path: "infra/cf-zones/hanzo-ai.yaml", note: "the mail exchange records for the zone" }] },
{ id: "github", name: "GitHub", role: "processor", mark: "github", purpose: "Signing in with a Git\
Hub account, and reading the repository a customer asks us to build. The public mirror of our own so\
urce is a different relationship and no customer data is in it.", data: "For sign-in: the account id\
entifier and the profile fields that flow back. For a build: the contents of the repository a custom\
er connected, read with a token scoped to what they installed us on.", location: "United States", url: "\
https://github.com", terms: "https://github.com/customer-terms/github-data-protection-agreement", evidence: [
{ repo: "hanzoai/universe", path: "infra/k8s/kms-canonical-crs/hanzo/github-oauth-kms-sync.yaml", note: "\
the sign-in credentials" }, { repo: "hanzoai/universe", path: "infra/k8s/hanzo-build/ghcr-renew.yaml",
note: "the installation token minted to read a connected repository" }] }, { id: "square", name: "\
Square", role: "processor", mark: "square", purpose: "Taking payment. The card is entered into a fra\
me they serve, so the number itself goes from the browser to them and never through us.", data: "The\
buyer's identity and the amount. The card number reaches them directly and does not reach us at any\
point.", location: "United States", url: "https://squareup.com", terms: "https://squareup.com/legal\
/general/dpa", evidence: [{ repo: "hanzoai/universe", path: "charts/app/values/hanzo/pay.yaml", note: "\
the origins the checkout page is permitted to reach" }, { repo: "hanzoai/cloud", path: "apps/integra\
tions/payments.go", note: "the endpoint" }] }, { id: "slack", name: "Slack", role: "processor", purpose: "\
Where operational alerts are posted, and the app a customer can install into their own workspace.", data: "\
The text of an alert, which names a service and a condition. For an installed app, whatever the cust\
omer's own workspace sends it.", location: "United States", url: "https://slack.com", terms: "https:\
//slack.com/trust/data-management/data-processing-addendum", evidence: [{ repo: "hanzoai/universe", path: "\
infra/k8s/kms-canonical-crs/hanzo/cloud-slack-kms-sync.yaml", note: "the app credentials and the sig\
ning secret" }, { repo: "hanzoai/universe", path: "charts/app/values/hanzo/cloud.yaml", note: "the c\
hannel alerts are addressed to" }] }, { id: "amazon", name: "Amazon Web Services", role: "vendor", purpose: "\
Named in configuration and not used. Their command-line tool is the client we run against our OWN ob\
ject store, and the region string beside it is a value that client requires rather than a place anyt\
hing is stored. The managed-model endpoint is written down and left switched off.", data: "None. Eve\
ry bucket those commands address resolves inside the cluster, and the model endpoint is commented ou\
t rather than configured.", location: "Not applicable", url: "https://aws.amazon.com", evidence: [{ repo: "\
hanzoai/universe", path: "infra/k8s/git/create-bucket-job.yaml", note: "the tool, run against the in\
-cluster store" }, { repo: "hanzoai/universe", path: "charts/app/values/enso/enso.yaml", note: "the \
managed-model endpoint, commented out and marked as waiting on credentials" }] }, { id: "googleads",
name: "Google Ads", role: "vendor", mark: "googleads", purpose: "Advertising we buy. Buying an adv\
ertisement is a purchase from a platform, not a route data travels.", data: "None. No script of thei\
rs is served by any Hanzo surface, no identifier is shared with them, and nothing about a visitor to\
our sites reaches them. `bin/surface` is what keeps that true rather than remembered.", location: "\
Not applicable", url: "https://ads.google.com", evidence: [{ repo: "hanzoai/trust", path: "bin/surfa\
ce.mjs", note: "refuses the build if a measurement tag of theirs is ever reached from a Hanzo surfac\
e" }] }, { id: "meta", name: "Meta", role: "vendor", mark: "meta", purpose: "Advertising we buy. Sam\
e relationship as the platform above and the same boundary.", data: "None. No script of theirs is se\
rved by any Hanzo surface, and no visitor event is sent to them.", location: "Not applicable", url: "\
https://www.facebook.com/business", evidence: [{ repo: "hanzoai/trust", path: "bin/surface.mjs", note: "\
refuses the build if their tag is ever reached from a Hanzo surface" }] }];
// check.mjs
var ID = /^[a-z0-9]+(\.[a-z0-9-]+)+$/;
var STATUS = ["automated", "partial", "absent"];
var STRENGTH = ["full", "partial"];
var METHOD = ["read", "test", "audit"];
var CLAIMS = [
/\bcertifi(ed|cation)\b/i,
/\baccredit(ed|ation)\b/i,
/\battest(ed|ation)\b/i,
/\bcompliant\b/i,
/\bcompliance with\b/i,
/\baudited by\b/i,
/\bfips[\s-]?(140[\s-]?[23][\s-]?)?(validated|certified|compliant|approved)\b/i
];
var FRAMEWORK_WORDS = [
/\bsoc[\s-]?[12]\b/i,
/\biso[\s-]?270\d\d\b/i,
/\b800[\s-]?53\b/i,
/\bfedramp\b/i,
/\bpci[\s-]?dss\b/i,
/\bhitrust\b/i,
/\bhipaa\b/i,
/\bcsa[\s-]?star\b/i
];
var isStr = (v) => typeof v === "string" && v.trim() !== "";
var isArr = (v) => Array.isArray(v) && v.length > 0;
function prose(control2) {
const out = [];
const walk = (v) => {
if (typeof v === "string") out.push(v);
else if (Array.isArray(v)) v.forEach(walk);
else if (v && typeof v === "object") {
for (const k of Object.keys(v)) {
if (k === "maps" || k === "id") continue;
walk(v[k]);
}
}
};
walk(control2);
return out.join("\n");
}
function checkPlace(place, where, problems) {
if (!place || typeof place !== "object") {
problems.push(`${where}: not an object`);
return;
}
if (!isStr(place.repo)) problems.push(`${where}: repo is required`);
if (!isStr(place.path)) problems.push(`${where}: path is required`);
else if (/^\//.test(place.path) || /\s/.test(place.path) || /(^|\/)\.\.?(\/|$)/.test(place.path)) {
problems.push(`${where}: path must be repo-relative with no spaces (got "${place.path}")`);
}
if (place.line !== void 0 && (!Number.isInteger(place.line) || place.line < 1)) {
problems.push(`${where}: line must be a positive whole number when present`);
}
}
function checkFrameworks(frameworks2) {
const problems = [];
const index = /* @__PURE__ */ new Set();
if (!frameworks2 || typeof frameworks2 !== "object" || Array.isArray(frameworks2)) {
return [["frameworks.json: expected an object keyed by framework id"], index];
}
for (const id of Object.keys(frameworks2)) {
if (id.charAt(0) === "$") continue;
const f = frameworks2[id];
const at = `frameworks.json[${id}]`;
if (!/^[a-z0-9]+$/.test(id)) problems.push(`${at}: id must be lowercase alphanumeric`);
if (!f || typeof f !== "object") {
problems.push(`${at}: not an object`);
continue;
}
for (const k of ["name", "publisher", "edition"]) {
if (!isStr(f[k])) problems.push(`${at}: ${k} is required`);
}
if (!isStr(f.unit)) problems.push(`${at}: unit is required \u2014 a count with no unit is not a fac\
t`);
if (!isStr(f.units)) problems.push(`${at}: units (the plural) is required`);
if (!isArr(f.clauses)) {
problems.push(`${at}: clauses must be a non-empty array`);
continue;
}
const seen = /* @__PURE__ */ new Set();
for (const c of f.clauses) {
if (!c || typeof c !== "object" || !isStr(c.id)) {
problems.push(`${at}: a clause has no id`);
continue;
}
if (seen.has(c.id)) problems.push(`${at}: duplicate clause ${c.id}`);
seen.add(c.id);
if (!isStr(c.title)) problems.push(`${at}.${c.id}: title is required`);
if (c.group !== void 0 && !isStr(c.group)) {
problems.push(`${at}.${c.id}: group must be a non-empty string when present`);
}
index.add(`${id}:${c.id}`);
}
}
return [problems, index];
}
function checkControls(input, clauseIndex) {
const problems = [];
const controls2 = Array.isArray(input) ? input : input && input.controls;
if (!Array.isArray(controls2)) {
return ["controls.json: expected a list of controls, or an envelope holding one"];
}
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < controls2.length; i++) {
const c = controls2[i];
const at = `controls.json[${c && c.id || i}]`;
if (!c || typeof c !== "object") {
problems.push(`${at}: not an object`);
continue;
}
if (!isStr(c.id)) problems.push(`${at}: id is required`);
else if (!ID.test(c.id)) problems.push(`${at}: id must be dotted lowercase, e.g. iam.pkce.s256`);
else if (seen.has(c.id)) problems.push(`${at}: duplicate id`);
if (isStr(c.id)) seen.add(c.id);
for (const k of ["claim", "mechanism"]) {
if (!isStr(c[k])) problems.push(`${at}: ${k} is required`);
}
if (c.title !== void 0 && !isStr(c.title)) {
problems.push(`${at}: title must be a non-empty string when present`);
}
if (STATUS.indexOf(c.status) < 0) {
problems.push(`${at}: status must be one of ${STATUS.join(", ")}`);
}
if (c.status !== "automated" && !isStr(c.note)) {
problems.push(`${at}: status "${c.status}" requires a note saying what is missing`);
}
if (!isArr(c.enforced)) problems.push(`${at}: enforced must name at least one place`);
else c.enforced.forEach((p, n) => checkPlace(p, `${at}.enforced[${n}]`, problems));
if (!isArr(c.verified)) problems.push(`${at}: verified must name at least one check`);
else
c.verified.forEach((v, n) => {
const vat = `${at}.verified[${n}]`;
if (!v || typeof v !== "object" || METHOD.indexOf(v.method) < 0) {
problems.push(`${vat}: method must be one of ${METHOD.join(", ")}`);
return;
}
if (v.method === "audit") {
if (!isArr(v.actions) || !v.actions.every(isStr)) {
problems.push(`${vat}: an audit check must name the actions that evidence it`);
}
} else if (!isArr(v.at)) {
problems.push(`${vat}: a ${v.method} check must name where it is`);
} else {
v.at.forEach((p, m) => checkPlace(p, `${vat}.at[${m}]`, problems));
}
});
if (!isArr(c.maps)) problems.push(`${at}: maps must name at least one clause`);
else {
const refs = /* @__PURE__ */ new Set();
for (const m of c.maps) {
if (!m || typeof m !== "object" || !isStr(m.clause)) {
problems.push(`${at}: a mapping has no clause`);
continue;
}
if (STRENGTH.indexOf(m.strength) < 0) {
problems.push(`${at} -> ${m.clause}: strength must be one of ${STRENGTH.join(", ")}`);
}
if (!clauseIndex.has(m.clause)) {
problems.push(`${at} -> ${m.clause}: no framework declares that clause`);
}
if (refs.has(m.clause)) problems.push(`${at}: duplicate mapping to ${m.clause}`);
refs.add(m.clause);
}
}
const text = prose(c);
for (const re of CLAIMS) {
const hit = re.exec(text);
if (hit) problems.push(`${at}: prose claims "${hit[0]}" \u2014 this repo does not claim status`);
}
for (const re of FRAMEWORK_WORDS) {
const hit = re.exec(text);
if (hit) {
problems.push(
`${at}: prose names "${hit[0]}" \u2014 a framework belongs in maps, where it carries a number`
);
}
}
}
return problems;
}
var ROLES = ["processor", "vendor"];
var SLUG = /^[a-z0-9]+(-[a-z0-9]+)*$/;
function checkEvidence(e, where, problems) {
if (!e || typeof e !== "object") {
problems.push(`${where}: not an object`);
return;
}
const origin = typeof e.origin === "string" ? e.origin.trim() : "";
if (origin) {
if (!/^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$/.test(origin) || origin.indexOf(".") < 0) {
problems.push(`${where}: origin must be a bare hostname (got "${e.origin}")`);
}
if (e.repo !== void 0 || e.path !== void 0) {
problems.push(`${where}: an origin and a repository path are two kinds of evidence, not one \
entry`);
}
return;
}
checkPlace(e, where, problems);
}
function checkSubprocessors(input) {
const problems = [];
const rows = Array.isArray(input) ? input : input && input.subprocessors;
if (!Array.isArray(rows)) {
return ["subprocessors.json: expected a list of parties, or an envelope holding one"];
}
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < rows.length; i++) {
const s = rows[i];
const at = `subprocessors.json[${s && s.id || i}]`;
if (!s || typeof s !== "object") {
problems.push(`${at}: not an object`);
continue;
}
if (!isStr(s.id)) problems.push(`${at}: id is required`);
else if (!SLUG.test(s.id)) problems.push(`${at}: id must be a lowercase slug, e.g. digitalocea\
n`);
else if (seen.has(s.id)) problems.push(`${at}: duplicate id`);
if (isStr(s.id)) seen.add(s.id);
if (!isStr(s.name)) problems.push(`${at}: name is required`);
if (!isStr(s.purpose)) problems.push(`${at}: purpose is required \u2014 why this party is here`);
if (!isStr(s.data)) {
problems.push(`${at}: data is required \u2014 what customer data reaches this party, or that none\
does and why`);
}
if (s.mark !== void 0 && (!isStr(s.mark) || !SLUG.test(s.mark))) {
problems.push(`${at}: mark must be a lowercase slug naming a canonical mark`);
}
if (s.url !== void 0 && !isStr(s.url)) problems.push(`${at}: url must be a non-empty string wh\
en present`);
if (ROLES.indexOf(s.role) < 0) {
problems.push(`${at}: role must be one of ${ROLES.join(", ")}`);
} else if (s.role === "processor") {
if (!isStr(s.location)) {
problems.push(`${at}: a processor must state where it is \u2014 the data goes there`);
}
if (!isStr(s.terms)) {
problems.push(`${at}: a processor must name the terms it processes under`);
}
}
if (!isArr(s.evidence)) {
problems.push(`${at}: evidence must name at least one place or origin`);
} else {
s.evidence.forEach((e, n) => checkEvidence(e, `${at}.evidence[${n}]`, problems));
}
const text = prose(s);
for (const re of CLAIMS) {
const hit = re.exec(text);
if (hit) problems.push(`${at}: prose claims "${hit[0]}" \u2014 this repo does not restate another\
party's status`);
}
for (const re of FRAMEWORK_WORDS) {
const hit = re.exec(text);
if (hit) problems.push(`${at}: prose names "${hit[0]}" \u2014 a framework belongs in maps, where \
it carries a number`);
}
}
return problems;
}
// src/inventory.ts
var CONTROLS = define_CONTROLS_default;
var FRAMEWORKS = define_FRAMEWORKS_default;
var SUBPROCESSORS = define_SUBPROCESSORS_default;
var VERSION = "0.2.4";
function ids() {
const out = [];
for (const k in FRAMEWORKS) {
if (!Object.prototype.hasOwnProperty.call(FRAMEWORKS, k)) continue;
if (k.charAt(0) === "$") continue;
out.push(k);
}
return out.sort();
}
function framework(id) {
if (!id || id.charAt(0) === "$") return null;
return Object.prototype.hasOwnProperty.call(FRAMEWORKS, id) ? FRAMEWORKS[id] : null;
}
function control(id) {
for (let i = 0; i < CONTROLS.length; i++) {
if (CONTROLS[i].id === id) return CONTROLS[i];
}
return null;
}
function verified(c) {
for (let i = 0; i < c.verified.length; i++) {
const m = c.verified[i].method;
if (m === "test" || m === "audit") return true;
}
return false;
}
function evidence(c) {
for (let i = 0; i < c.verified.length; i++) {
const v = c.verified[i];
if (v.method === "audit" && v.actions && v.actions.length) return v.actions;
}
return [];
}
// src/db.ts
function db() {
const d = globalThis.__db;
return d && typeof d.query === "function" ? d : null;
}
function own() {
const f = globalThis.__own;
return typeof f === "function" ? f() : false;
}
function newId() {
const f = globalThis.__newId;
if (typeof f !== "function") throw new Error("trust: host injected no __newId");
return f();
}
var KINDS = [
"profile",
"control",
"document",
"subprocessor",
"policy",
"faq",
"update",
"risk"
];
function isKind(s) {
for (let i = 0; i < KINDS.length; i++) if (KINDS[i] === s) return true;
return false;
}
var SINGLE = { profile: true, risk: true };
function parse(r) {
let data = {};
try {
const v = JSON.parse(r.data);
if (v && typeof v === "object" && !Array.isArray(v)) data = v;
} catch (e) {
data = {};
}
return { id: r.id, ord: Number(r.ord) || 0, updated: Number(r.updated) || 0, data };
}
function list(kind) {
const d = db();
if (!d) return [];
const rows = d.query(
"SELECT kind, id, ord, data, updated FROM record WHERE kind = ? ORDER BY ord ASC, id ASC",
[kind]
);
const out = [];
for (let i = 0; i < rows.length; i++) out.push(parse(rows[i]));
return out;
}
function get(kind, id) {
const d = db();
if (!d) return null;
const rows = d.query(
"SELECT kind, id, ord, data, updated FROM record WHERE kind = ? AND id = ?",
[kind, id]
);
return rows.length ? parse(rows[0]) : null;
}
function put(kind, id, ord, data, at) {
const d = db();
if (!d) throw new Error("trust: no store");
d.exec(
"INSERT INTO record (kind, id, ord, data, updated) VALUES (?, ?, ?, ?, ?) ON CONFLICT(kind, id\
) DO UPDATE SET ord = excluded.ord, data = excluded.data, updated = excluded.updated",
[kind, id, ord, JSON.stringify(data), at]
);
}
function drop(kind, id) {
const d = db();
if (!d) throw new Error("trust: no store");
return d.exec("DELETE FROM record WHERE kind = ? AND id = ?", [kind, id]).changes;
}
// src/center.ts
var CATEGORIES = [
"infrastructure",
"data",
"access",
"network",
"endpoint",
"corporate",
"product",
"incident"
];
var KINDS2 = {
soc2: { label: "SOC 2 report", attested: true },
iso: { label: "ISO/IEC 27001 certificate", attested: true },
pentest: { label: "Penetration test report", attested: true },
letter: { label: "Auditor letter", attested: true },
caiq: { label: "CAIQ self-assessment", attested: false },
sig: { label: "SIG self-assessment", attested: false },
vsa: { label: "VSA self-assessment", attested: false },
questionnaire: { label: "Security questionnaire", attested: false },
policy: { label: "Policy", attested: false },
other: { label: "Document", attested: false }
};
var TIERS = ["public", "gated"];
var str = (v) => typeof v === "string" ? v.trim() : "";
var has = (v) => str(v) !== "";
function controls() {
const base = own() ? CONTROLS : [];
const seen = {};
for (let i = 0; i < base.length; i++) seen[base[i].id] = true;
const out = base.slice();
const rows = list("control");
for (let i = 0; i < rows.length; i++) {
const c = Object.assign({}, rows[i].data, { id: rows[i].id });
if (seen[c.id]) continue;
out.push(c);
}
return out;
}
function parties() {
const base = own() ? SUBPROCESSORS : [];
const seen = {};
for (let i = 0; i < base.length; i++) seen[base[i].id] = true;
const out = base.slice();
const rows = list("subprocessor");
for (let i = 0; i < rows.length; i++) {
const s = Object.assign({}, rows[i].data, { id: rows[i].id });
if (seen[s.id]) continue;
out.push(s);
}
return out;
}
var COMPILED = {
control: CONTROLS,
subprocessor: SUBPROCESSORS
};
function governed(kind, id) {
if (!own() || !id) return false;
const rows = Object.prototype.hasOwnProperty.call(COMPILED, kind) ? COMPILED[kind] : null;
if (!rows) return false;
for (let i = 0; i < rows.length; i++) if (rows[i].id === id) return true;
return false;
}
function validate(kind, id, data) {
switch (kind) {
case "control": {
const c = Object.assign({}, data, { id });
const [, index] = checkFrameworks(FRAMEWORKS);
const problems = checkControls([c], index);
const cat = str(data.category);
if (cat) {
let ok2 = false;
for (let i = 0; i < CATEGORIES.length; i++) if (CATEGORIES[i] === cat) ok2 = true;
if (!ok2) problems.push("category must be one of " + CATEGORIES.join(", "));
}
return problems;
}
case "document": {
const p = [];
if (!has(data.title)) p.push("title is required");
const k = str(data.kind) || "other";
if (!Object.prototype.hasOwnProperty.call(KINDS2, k)) {
p.push("kind must be one of " + Object.keys(KINDS2).join(", "));
return p;
}
const tier = str(data.tier) || "gated";
if (TIERS.indexOf(tier) < 0) p.push("tier must be public or gated");
if (KINDS2[k].attested && tier === "public") {
p.push(
"a " + KINDS2[k].label + " is attested by someone outside this organization, so it is re\
leased through a grant rather than published"
);
}
return p;
}
// The SAME module the build gate runs, so an authored party is held to the
// rule a committed one is: it must take a role from the closed pair, say
// what customer data reaches it, and — if it is a processor — say where it
// is and under which terms. A name and a sentence is not a disclosure.
case "subprocessor":
return checkSubprocessors([Object.assign({}, data, { id })]);
case "policy":
return has(data.title) ? [] : ["title is required"];
case "faq": {
const p = [];
if (!has(data.question)) p.push("question is required");
if (!has(data.answer)) p.push("answer is required");
return p;
}
case "update": {
const p = [];
if (!has(data.title)) p.push("title is required");
if (!has(data.at)) p.push("at is required \u2014 an update with no date cannot be read in order");
return p;
}
case "profile":
return has(data.name) ? [] : ["name is required"];
case "risk":
return Array.isArray(data.items) ? [] : ["items must be a list of {label, value}"];
}
return ["unknown section " + kind];
}
function write(kind, id, body, at, ord) {
if (!isKind(kind)) return { ok: false, status: 404, message: "no trust section " + kind };
const k = kind;
if (!body || typeof body !== "object" || Array.isArray(body)) {
return { ok: false, status: 400, message: "body must be an object" };
}
const data = Object.assign({}, body);
let key = SINGLE[k] ? "" : str(id) || newId();
delete data.id;
if (governed(k, key)) {
return {
ok: false,
status: 409,
message: k + " " + key + " is part of this deployment's own inventory, which is governed in \
git and cannot be authored through the API"
};
}
const errors = validate(k, key, data);
if (errors.length) {
return { ok: false, status: 400, message: "the " + k + " is malformed", errors };
}
if (k === "document" && !has(data.tier)) data.tier = "gated";
put(k, key, ord, data, at);
return { ok: true, wrote: { kind: k, id: key, updated: at } };
}
function remove(kind, id) {
if (!isKind(kind)) return { ok: false, status: 404, message: "no trust section " + kind };
const k = kind;
if (governed(k, id)) {
return {
ok: false,
status: 409,
message: k + " " + id + " is part of this deployment's own inventory and is removed by a com\
mit, not by a request"
};
}
const n = drop(k, SINGLE[k] ? "" : id);
if (!n) return { ok: false, status: 404, message: "no " + k + " " + id };
return { ok: true, status: 200, message: "" };
}
function shaped(rows) {
const out = [];
for (let i = 0; i < rows.length; i++) {
const r = Object.assign({}, rows[i].data);
r.id = rows[i].id;
r.updated = rows[i].updated;
out.push(r);
}
return out;
}
function section(kind) {
return shaped(list(kind));
}
function single(kind) {
const r = get(kind, "");
if (!r) return null;
const out = Object.assign({}, r.data);
out.updated = r.updated;
return out;
}
function document(d, grant) {
const k = str(d.kind) || "other";
const meta = Object.prototype.hasOwnProperty.call(KINDS2, k) ? KINDS2[k] : KINDS2.other;
const tier = str(d.tier) || "gated";
const open = tier === "public" || grant;
const out = {
id: d.id,
title: d.title,
kind: k,
label: meta.label,
attested: meta.attested,
tier,
updated: d.updated
};
if (has(d.note)) out.note = d.note;
if (open && has(d.href)) out.href = d.href;
out.released = open;
return out;
}
function documents(grant) {
const rows = section("document");
const out = [];
for (let i = 0; i < rows.length; i++) out.push(document(rows[i], grant));
return out;
}
// src/coverage.ts
var RANK = { none: 0, partial: 1, automated: 2 };
function worth(c, m) {
if (c.status === "absent") return "none";
if (c.status === "partial") return "partial";
if (m.strength !== "full") return "partial";
return verified(c) ? "automated" : "partial";
}
function inventory(cs = CONTROLS) {
let automated = 0;
let partial = 0;
let absent = 0;
let unverified = 0;
for (let i = 0; i < cs.length; i++) {
const c = cs[i];
if (c.status === "automated") automated++;
else if (c.status === "partial") partial++;
else absent++;
if (!verified(c)) unverified++;
}
return {
total: cs.length,
automated,
partial,
absent,
unverified,
statement: automated + " of " + cs.length + " controls automated, " + partial + " partial, " +
absent + " absent" + (unverified ? "; " + unverified + " rest on a reading rather than a test" :
"")
};
}
function coverClause(clause, id, cs) {
let level = "none";
const hits = [];
const ref = id + ":" + clause.id;
for (let i = 0; i < cs.length; i++) {
const c = cs[i];
for (let j = 0; j < c.maps.length; j++) {
const m = c.maps[j];
if (m.clause !== ref) continue;
const got = worth(c, m);
if (got === "none") continue;
if (RANK[got] > RANK[level]) level = got;
hits.push({ id: c.id, rank: RANK[got] });
}
}
hits.sort((a, b) => b.rank - a.rank || (a.id < b.id ? -1 : 1));
const out = {
id: clause.id,
title: clause.title,
level,
controls: hits.map((h) => h.id)
};
if (clause.group) out.group = clause.group;
return out;
}
function summarize(id, f, cs) {
const clauses = [];
const tally = { total: f.clauses.length, automated: 0, partial: 0, none: 0 };
for (let i = 0; i < f.clauses.length; i++) {
const cc = coverClause(f.clauses[i], id, cs);
clauses.push(cc);
tally[cc.level]++;
}
const detail = {
framework: id,
name: f.name,
publisher: f.publisher,
edition: f.edition,
unit: f.unit,
units: f.units,
total: tally.total,
automated: tally.automated,
partial: tally.partial,
none: tally.none,
statement: tally.automated + " of " + tally.total + " " + f.units + " have an automated contro\
l, " + tally.partial + " partial, " + tally.none + " none",
clauses
};
if (f.note) detail.note = f.note;
return detail;
}
function framework2(id, cs = CONTROLS) {
const f = framework(id);
return f ? summarize(id, f, cs) : null;
}
function summary(cs = CONTROLS) {
const list2 = ids();
const out = [];
for (let i = 0; i < list2.length; i++) {
const d = summarize(list2[i], framework(list2[i]), cs);
const row = {
framework: d.framework,
name: d.name,
publisher: d.publisher,
edition: d.edition,
unit: d.unit,
units: d.units,
total: d.total,
automated: d.automated,
partial: d.partial,
none: d.none,
statement: d.statement
};
if (d.note) row.note = d.note;
out.push(row);
}
return { controls: inventory(cs), frameworks: out };
}
// src/host.ts
function now() {
return globalThis.__now();
}
function audit(q) {
const fn = globalThis.__audit;
if (typeof fn !== "function") return null;
return fn(q);
}
// src/reply.ts
var ok = (body) => ({ status: 200, body });
var badReq = (message) => ({
status: 400,
body: { success: false, message }
});
var notFound = (message, extra) => ({
status: 404,
body: extra ? __spreadValues({ success: false, message }, extra) : { success: false, message }
});
var readOnly = (message) => ({
status: 405,
body: { success: false, message, allow: "GET" }
});
var unmounted = (message) => ({
status: 501,
body: { success: false, message }
});
// src/evidence.ts
var LIMIT = 100;
var MAX = 1e3;
var DATE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$/;
function bound(v, field, errs) {
const s = v || "";
if (s && !DATE.test(s)) errs.push(field + " must be an RFC 3339 date");
return s;
}
function query(ctx) {
const id = ctx.params.control || "";
if (!id) return badReq("control is required");
const c = control(id);
if (!c) return notFound("no control " + id + " in the inventory");
const errs = [];
const from = bound(ctx.params.from, "from", errs);
const to = bound(ctx.params.to, "to", errs);
let limit = LIMIT;
const raw = ctx.params.limit;
if (raw !== void 0 && raw !== "") {
const n = Number(raw);
if (!isFinite(n) || Math.floor(n) !== n || n < 1) {
errs.push("limit must be a positive whole number");
} else {
limit = n > MAX ? MAX : n;
}
}
if (from && to && from >= to) errs.push("from must be before to");
if (errs.length) return badReq(errs.join("; "));
const actions = evidence(c);
const head = {
control: c.id,
claim: c.claim,
status: c.status,
verified: verified(c),
actions,
from,
to,
limit
};
if (actions.length === 0) {
return ok({
control: head.control,
claim: head.claim,
status: head.status,
verified: head.verified,
actions,
queried: false,
total: 0,
note: "no audit action evidences this control; it is verified by reading the source at the f\
iles the inventory names, not from the trail",
enforced: c.enforced
});
}
const trail = audit({ actions, from, to, limit });
if (!trail) {
return unmounted(
"this deployment mounted trust without __audit, so the trail cannot be read"
);
}
return ok({
control: head.control,
claim: head.claim,
status: head.status,
verified: head.verified,
actions,
from,
to,
limit,
queried: true,
total: trail.total,
count: trail.rows.length,
truncated: trail.total > trail.rows.length,
rows: trail.rows
});
}
// src/index.ts
function frameworks() {
return ids().map((id) => {
const f = framework(id);
return {
framework: id,
name: f ? f.name : "",
publisher: f ? f.publisher : "",
edition: f ? f.edition : "",
unit: f ? f.unit : "",
units: f ? f.units : "",
total: f ? f.clauses.length : 0
};
});
}
function find(id) {
const cs = controls();
for (let i = 0; i < cs.length; i++) if (cs[i].id === id) return cs[i];
return control(id);
}
function center(org, grant) {
const cs = controls();
const s = summary(cs);
const profile = single("profile") || {};
return {
version: VERSION,
generated: now(),
org,
profile,
controls: cs,
coverage: s.frameworks,
inventory: s.controls,
frameworks: frameworks(),
documents: documents(grant),
subprocessors: parties(),
policies: section("policy"),
faq: section("faq"),
updates: section("update"),
risk: single("risk") || { items: [] }
};
}
var routes = {
health: () => ok({ service: "trust", status: "ok", version: VERSION }),
center: (ctx) => ok(center(ctx.org, true)),
// The public door. It answers only for an organization that has said its
// centre is public: an unpublished one is NOT FOUND rather than empty, because
// an empty centre and a centre nobody meant to show read the same and are not
// the same. No credential reaches here, so nothing gated does either.
published: (ctx) => {
const p = single("profile");
if (!p || p.published !== true) {
return notFound("no published trust centre for " + ctx.org);
}
return ok(center(ctx.org, false));
},
"profile.get": () => ok(single("profile") || {}),
"controls.list": () => {
const cs = controls();
return ok(__spreadProps(__spreadValues({ version: VERSION }, summary(cs).controls), { controls: cs }));
},
"controls.get": (ctx) => {
const c = find(ctx.params.id || "");
return c ? ok(c) : notFound("no control " + (ctx.params.id || "") + " in this trust centre");
},
"frameworks.list": () => ok({ frameworks: frameworks() }),
"coverage.list": () => {
const cs = controls();
return ok(__spreadValues({ version: VERSION, generated: now() }, summary(cs)));
},
"coverage.get": (ctx) => {
const id = ctx.params.framework || "";
const d = framework2(id, controls());
if (!d) return notFound("no framework " + id + " is mapped", { frameworks: ids() });
return ok(__spreadValues({ version: VERSION, generated: now() }, d));
},
"documents.list": () => ok({ documents: documents(true) }),
"subprocessors.list": () => ok({ subprocessors: parties() }),
"policies.list": () => ok({ policies: section("policy") }),
"faq.list": () => ok({ faq: section("faq") }),
"updates.list": () => ok({ updates: section("update") }),
"risk.get": () => ok(single("risk") || { items: [] }),
"evidence.get": query,
// ONE write route for every section. What may be in a record is decided by
// center.validate, which runs the SAME module the build gate runs, so a
// control authored here is held to the rule a committed one is held to.
"section.put": (ctx, req) => {
const kind = ctx.params.kind || "";
const at = now();
const env = req.body || {};
const ord = typeof env.ord === "number" ? env.ord : 0;
const r = write(kind, ctx.params.id || "", env.data, at, ord);
if (!r.ok) {
const body = { success: false, message: r.message };
if (r.errors) body.errors = r.errors;
return { status: r.status, body };
}
return ok(r.wrote);
},
"section.delete": (ctx) => {
const r = remove(ctx.params.kind || "", ctx.params.id || "");
if (!r.ok) return { status: r.status, body: { success: false, message: r.message } };
return ok({ kind: ctx.params.kind, id: ctx.params.id, deleted: true });
}
};
var WRITES = {
"section.put": "PUT",
"section.delete": "DELETE"
};
var READ_ONLY = "the trust inventory is read here: this deployment's own controls are build-time d\
ata governed in git, and the audit trail is written by the platform.";
globalThis.handle = (req) => {
req = req || {};
const route = req.route || "";
const fn = routes[route];
if (!fn) return notFound("no trust route " + route);
const method = (req.method || "GET").toUpperCase();
const wants = WRITES[route];
if (wants) {
if (method !== wants) return readOnly(READ_ONLY);
} else if (method !== "GET" && method !== "HEAD") {
return readOnly(READ_ONLY);
}
const org = req.orgId || "";
if (route !== "health" && !org) {
return { status: 401, body: { success: false, message: "missing tenant" } };
}
const params = {};
const bags = [req.params, req.query];
for (let i = 0; i < bags.length; i++) {
const bag = bags[i];
if (!bag) continue;
for (const k in bag) {
if (Object.prototype.hasOwnProperty.call(bag, k)) params[k] = bag[k];
}
}
try {
return fn({ org, params }, req);
} catch (err) {
return {
status: 500,
body: { success: false, message: String(err && err.message || err) }
};
}
};
globalThis.trust = {
version: VERSION,
controls: () => controls(),
frameworks: () => ids(),
summary,
framework: framework2
};
})();