AuthBox documentation — Technical documentation

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:

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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:

  1. 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.
  2. Bring the deployment up from configuration alone. authboxctl init writes 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.
  3. 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).