Documentation embedded in this build.
AuthBox — the deployment as data
This is the answer to one question, asked before a deployment starts and again every time it changes: do we need a code change for this? Every row below says where a noun lives — a setting with its tier, a section of the dataset, or a document the deployment supplies — and the last section says what stays code, with the reason.
It is generated. The setting paths are resolved against the settings register, the dictionary's grammar is read out of the type that defines it, and the dataset's sections are read off the document struct — so a noun whose home was renamed fails the build rather than sending an integrator somewhere that no longer exists. make docs-check holds it current.
Two rules make the whole page true, and they are worth reading before the table:
The dictionary is the deployment's, not the product's. Nothing under cmd/ or internal/ declares an attribute, a level, a compartment or a country. A test refuses the demonstration dataset's own words as string literals in this product's Go — see docs/assurance/vocabulary.yaml's deployment-vocabulary section — so "no code changes" stays true after the day somebody checks it.
A default is data with a name. Where a setting's default is a word, the word is the demonstration dataset's and the setting is how another deployment says its own. A default is never a fact about the product.
1. The dictionary: the grammar a vocabulary is written in
One entry per attribute, in the dataset's attributes: section. These are the fields an entry may carry, by the names a file writes them under:
| Field | |
|---|---|
attribute |
the name, which is this deployment's word and appears nowhere in the product |
kind |
how its values compare, and therefore which operators a restriction may use |
order |
the ascending total order, for an ordered attribute: the ladder itself |
values |
the closed vocabulary, for a scalar or a set. A value outside it is a load error rather than a non-match, which turns a typo into a refused start instead of a silent denial |
authority |
who owns the values: this deployment, or an upstream that asserts them |
bridge |
what this deployment will treat each of the upstream's OWN values as, applied once at adoption so every stored value is in this dictionary and every comparison stays in one. Only on an attribute declaring an authority; proved total, onto this attribute's own vocabulary, and monotone for an ordered one, and refused at load naming the offending pair otherwise |
upstream_values |
the upstream's own vocabulary the bridge must be total over — ascending, for an ordered attribute, because that is the upstream's ladder and an order is a list. Declared rather than read off the bridge's keys, or "total" would have no domain but the map itself and "monotone" no order to be monotone with respect to. A value outside it is refused at adoption, whole, naming the value and the upstream |
marking |
the direction it compares in when DATA carries it. Empty means it may not appear in a marking |
sources |
the distinguished-name components that supply it, which makes it certificate-derived and not overridable by a store |
The closed vocabularies those fields draw from, read from the constants that define them:
- kinds —
scalar,set,ordered,date - comparison directions —
dominates(the data's value must be matched or out-ranked by the subject's) andadmits(the data names who may receive it). An attribute with neither may not appear in a marking at all, which is the fail-closed default: the direction cannot be inferred from the kind, because a compartment and a releasability list are both sets that compare in OPPOSITE directions - authority —
local, or the name of an upstream that owns the values. A local write to an upstream-owned attribute is refused at the point of the write, naming the authority - the value bridge — where an upstream spells a value differently, the consumer's own file says what it will treat each of the upstream's values as. It is the only way two deployments with different ladder NAMES align, and alignment is explicit, per attribute, in the consumer's file, or it does not happen: an upstream may assert only what the consumer said it owns, and a value the consumer has no reading of is refused rather than ignored
2. The dataset: the records a deployment authors
Every section a dataset document may carry, read off the document type itself:
schema, version, default_issuer, rehearsal, attributes, entities, missions, mission_tags, invitations, grants, requests, attestation_authorities, federation_bundles, obligations, names, goals, ssh_resources, subjects
A dataset is one or more files loaded together, so a deployment adds its own beside whatever a first boot wrote rather than editing that file.
3. Every noun, and where it lives
The attribute dictionary
Every attribute name, its kind, the direction it compares in when data carries it, its closed vocabulary, the distinguished-name components that supply it, and which authority owns its values. Nothing in this product declares an attribute; a deployment declares all of them.
- Where: the dataset's
attributes:section - Read: operations/marked-data.md
Another organisation's vocabulary, mapped onto this one
bridge and upstream_values on an attribute an upstream owns: what this deployment will treat each of that upstream's own values as. Applied once, at adoption, so every stored value is in this dictionary and every comparison afterwards runs in one. It cannot widen — it is the consumer's statement, in the consumer's file, and a value it does not name is refused rather than stored.
- Where: the dataset's
attributes:section - Read: operations/sync-engine.md
The classification ladder
The names and the order of the levels, as the order of an ordered attribute marked dominates. A first boot SURVEYS the dictionary for one and ships none — and refuses to choose when a dictionary declares two, naming both.
- Where: the dataset's
attributes:section - Read: operations/store.md
Compartments, communities of interest, access limits, releasability
Flat set attributes with a closed values list: they match, they do not rank. A negative clause (none_of) requires the closed vocabulary, because an exclusion cannot tell "not in this compartment" from a misspelling of it.
- Where: the dataset's
attributes:section - Read: operations/marked-data.md
Which attribute is the classification, and which is the affiliation
The object store's shelves and the door in front of them read the level and the organisation off a marking, and these name the two dictionary entries they read. The defaults are the demonstration dataset's words; a deployment with another ladder sets all four to its own.
- Where:
store.clearance_attribute(tier 1, defaultclearance);store.affiliation_attribute(tier 1, defaultaffiliation);proxy.store_scopes.clearance_attribute(tier 1, defaultclearance);proxy.store_scopes.affiliation_attribute(tier 1, defaultaffiliation) - Read: operations/store.md
Missions, restrictions, markings, floors, owners, tags
What each mission requires of a subject, what marking it carries, who owns it and which tag vocabulary groups it. A mission is a record, so authoring one is a write rather than a release.
- Where: the dataset's
missions:section, and the dataset'smission_tags:section - Read: operations/marked-data.md
Entities, groups, memberships and readership
Who exists, which groups hold them, who may see a group, and which missions a group binds. The population is data in every topology, federated or not.
- Where: the dataset's
entities:section - Read: operations/federation.md
The authorization map: what each operation requires
The requirement on every operation of every surface: the mission, the group, the attribute clauses and the signature rule. A deployment that wants different requirements supplies its own document; the server refuses to start when the document and the dataset disagree.
- Where: this deployment's own OpenAPI document, named by
spec.path, with anx-authboxblock per operation (the shipped one is the default and nothing more), andspec.surfaces(tier 0);spec.publish_redacted(tier 1, defaultfalse) - Read: ARCHITECTURE.md
The certificate authority: certificate, keystore, active and retired keys, custody
Which key signs, where its private half lives — a file, a passphrase, or a PKCS#11 token — and which retired keys still verify.
- Where:
ca.*— 7 settings, tier 0/1/2 - Read: operations/hsm.md
Issuance profiles: validity, key usages, policy OIDs, attestation, invitations
What a credential this deployment issues looks like, who may ask for one, and what they must prove first. A profile is a configuration block, so a second shape of credential is a block rather than a branch.
- Where:
enroll.*— 11 settings, tier 0/1 - Read: operations/first-boot.md
Identity resolution: name shape, issuer pinning, deny list, group certificates
How a presented chain becomes a subject: which distinguished-name components count, which issuers are pinned to which records, and what is refused outright.
- Where:
identity.default_issuer(tier 0);identity.deny_list(tier 1);identity.dn_override(tier 2, defaultdeny);identity.groups_may_authenticate(tier 1, defaultfalse);identity.require_issuer(tier 1, defaultfalse);identity.require_subject_record(tier 1, defaultfalse);xpe.dn_order(tier 2, defaultrfc4514) - Read: operations/troubleshooting-forbidden.md
The clearance authority's dialects
The attribute-authority namespace this deployment names itself by, and the parameters of the codec it answers a standard dialect in. There is no default namespace: a globally unique name is a promise only its holder can make.
- Where:
clearance.*— 2 settings, tier 1 - Read: operations/marked-data.md
A customer's own document as one of this deployment's authorization maps
OPERATIONS DECLARED HERE AND SERVED ELSEWHERE (docs/plans/175 §2). The writer loads such a document with the same parser and the same dataset validation as its own, merges its operations into the one authorization map under the DOCUMENT's own servers[0].url prefix, and mounts a handler for none of them — so the requirement a customer wrote in a file they own is enforced by this deployment, and everything that reads the map reads their operations too. It is not spec.surfaces: that setting describes routes this listener SERVES. A document declaring an address this listener does serve is refused at load.
- Where: a customer's own OpenAPI 3.1 document, named by
spec.foreign, carrying anx-authboxblock per operation exactly as this deployment's own document does, andspec.foreign(tier 1) - Read: guide/dialects.md
The name and the address a customer's API answers under here
What the customer's people call this API — on the door's tile, in the sidecar's logs and liveness body, defaulting to the document's own info.title — and the prefix those operations are served at HERE, which is not always the one their document was written for. Neither moves what is enforced: the authorization map keeps the DOCUMENT's own prefix, so a sidecar can serve one address and ask about another, and the mapping addresses operations by id rather than by URL.
- Where:
dialect.service.name(tier 0);dialect.service.base_url(tier 0) - Read: guide/dialects.md
The API shape a customer's own applications already read
A DIALECT: the customer's own openapi.yaml, unmodified, and one dialect.yaml mapping AuthBox's operations onto theirs — five rule kinds, no expression, no conditional and no code. It is proved at load against BOTH documents by six rules, so a mapping that could widen a decision or shed a marking is a refusal to start naming the rule. A shape this grammar cannot express needs code, and the honest home for that is a private adapter on sdk/foreign, out of tree, under the no-widening harness. Every dialect this deployment declares is proved and counted in DIALECTS.md.
- Where:
dialect.*— 16 settings, tier 0/1/2 - Read: guide/dialects.md
The directory this deployment publishes
Whether the read-only directory surface answers at all, and on which certificate. What it publishes is projected from the dataset, never configured twice.
- Where:
ldap.*— 6 settings, tier 0/1 - Read: operations/posture.md
4. What stays code, and why
Two things, and stating them is the point: a page claiming everything is data would be read as a promise and then found wrong about exactly these.
The well-known mission identifiers
The shipped authorization map names them, and internal/spec refuses to start when a declared requirement names a mission the dataset does not hold — so a deployment that renamed one would be a deployment that does not boot. They are local-only entitlements a front door and a replica need, minted by a first boot and bound by nobody; a deployment renames none of them and needs to rename none.
They are: authbox-bootstrap, authbox-read, authbox-administration, authbox-enrollment, authbox-replica, authbox-door-audit, authbox-topology, authbox-can-others
The cryptographic posture
Which algorithms exist is decided by the module the binary is LINKED against, not by a file: a setting that turned an approved posture off would be a setting whose value contradicts the validation certificate the deployment relies on. A deployment that needs both postures runs two processes, because one process is one posture.
5. How a deployment proves it for itself
Three things, in the order they cost least:
- Read this page's rows against your own configuration. A noun you cannot find here is a question for us, not a code change you should be planning.
- Bring the deployment up from configuration alone.
authboxctl initwrites a seed against the dictionary your directory already declares — including the object store's shelves, one per level of whatever ladder you named — and refuses to guess when your dictionary declares two. - Run the rehearsal. A generated population acting against a live deployment is the only proof that a vocabulary nobody here has ever seen decides what it should (operations/rehearsal.md).