Saphan StudioDocs
Reference/Commands

saphan decision

Files signed decision records and governance documents into the law store, reads them back, and keeps the workspace's bridge (its shared git record) in step with them.

Files signed decision records and governance documents into the law store, reads them back, and keeps the workspace's bridge (its shared git record) in step with them. Each text is stored at an address made of its citation key and the SHA-256 of its exact bytes, so you can always tell which text was in force at a given time. Keys come in four families: architecture decision records (adr-0001), governance documents (gov-<slug>), conventions (conv-<slug>), and other canon documents addressed by their path (for example skills/lang-go/SKILL.md). A stored text binds only when it is signed, verifies, and has the status RATIFIED; a PROPOSAL is a recommendation.

Filing is three steps, and the signing key never leaves the machine that holds it:

saphan decision canonical ADR.md --decision adr-0001 ...   # any machine: print the exact bytes to sign
saphan decision sign ADR.md --root-key ... --out env.txt   # the machine with the key: sign them
saphan decision file ADR.md --envelope-file env.txt ...    # carry the signature in and project it

On the machine that holds the root key, saphan decision file with --root-key and --actor signs and files in one step — except on an installation with a gateway, where --root-key on decision file is refused and the three-step form is the only one. On such an installation the filing runs on the gateway and lands in the gateway's bridge, so the --bridge and --checkout paths of file, project and census are paths on the gateway host, and saphan decision migrate must be run on the gateway host itself.

For every document except an architecture decision record, a filing is accepted only when its signer is, byte for byte, an owner named in the installation's root-signed deployment record; if no owner can be determined, the filing is refused. For an architecture decision record, any signer using the root key is accepted. Before anything is written, the installation's edition (read from the same deployment record) decides whether a customer's own version of a locked or team document is accepted (see saphan decision classes); if the edition cannot be read, every customer version is refused. A version signed by the vendor is accepted in every edition.

saphan decision

saphan decision canonical

Prints the exact signing subject for one filing: the address the text will be stored at, and the payload the signature must cover. It writes nothing and contacts nothing, so any machine can state precisely what is to be signed. Give saphan decision file the same key, status, filer and time afterwards, or the signature will not match.

saphan decision canonical <file>
flagtypedefaultwhat it does
--classstringThe document's mutability class: locked, team or open. Required for a path-addressed canon key, optional for governance and convention keys, refused for an architecture decision record; it is part of what is signed.
--decisionstringThe citation key this text is a version of, such as adr-0001, gov-<slug> or conv-<slug>, or a canon document's path in the bridge, which is converted to its key (required).
--filed-atstringThe time of the filing, as an RFC 3339 timestamp (required); it is part of what is signed, so the command never reads a clock for it.
--filed-bystringWho performs the filing (required); it is part of what is signed and cannot be changed afterwards.
--statusstringPROPOSAL, RATIFIED, SUPERSEDED or WITHDRAWN (required); only RATIFIED binds.
--titlestringA human-readable label; it is not part of the address and nothing is looked up by it.

saphan decision census

Compares the bridge's decisions/, governance/, conventions/ and skills/ directories against the law store, in both directions: stale or hand-edited files, admitted texts with no file, files for texts never admitted, and governance documents nobody has signed. With --checkout it measures another clone instead, for example one on a remote machine, to tell whether it is behind. Exit code 2 means it found a disagreement; 3 means it could not run (no bridge here, or the path is not a suitable checkout).

saphan decision census
flagtypedefaultwhat it does
--branchstringThe branch the bridge must already be on; checked, never switched to. Defaults to main.
--bridgestringAbsolute path to the bridge working tree; defaults to bridge inside the workspace.
--checkoutstringAbsolute path to another clone to compare instead of the bridge; it is only read. Needs both that path and the law store reachable from this machine.
--gitstringThe git executable to use; defaults to git on your PATH.
--verboseboolfalsePrint the full listing and remarks; a clean result otherwise prints a single line.

saphan decision classes

Prints the table that assigns each canon document path a class (locked, team or open) and the editions (solo, team, enterprise) that accept a customer's own version of it. A path the table does not name is open; the vendor's own versions are accepted in every edition.

saphan decision classes

saphan decision diff

Prints the newest unsigned draft of a canon document as a unified diff against the version the store currently accepts, which is what the owner reads before signing. Lines that are not part of the diff start with remark: . It writes nothing and reads no key. The baseline is whatever the store accepts now; the owner rule is checked only when a text is filed, so a version accepted under an older rule can still be the baseline.

saphan decision diff gov-<slug>
saphan decision diff <citation-key>

saphan decision draft

Stores an unsigned draft of a governance or convention document for the owner to review and sign. A draft never binds, is never written into the bridge, and its author and time are what you typed, not attested. Architecture decision records are not drafted; file them directly. Accepting the text is the owner's separate act with saphan decision file.

saphan decision draft <file>
flagtypedefaultwhat it does
--decisionstringThe citation key the text is a draft of (required); architecture decision records are refused.
--drafted-atstringThe time of drafting, as an RFC 3339 timestamp (required); the command reads no clock.
--drafted-bystringWho drafted the text (required); it is recorded as typed, not verified.
--titlestringA human-readable label; it is not part of the address.

saphan decision file

Files one text and its owner signature into the law store, verifying the signature before writing and refusing a pair that could never be read as valid. A filing without any signature is refused. On the machine that holds the root key, --root-key with --actor signs and files in one pass instead of taking an envelope; on an installation with a gateway that one-pass form is refused.

Before anything is written, two checks run: for every document except an architecture decision record, the signer must be exactly an owner named in the installation's root-signed deployment record (if no owner can be determined, the filing is refused); and the installation's edition decides whether a customer's own version of a locked or team document is accepted (an unreadable edition refuses every customer version; a vendor-signed version is accepted in every edition).

⚠ A filed text cannot be changed or removed: the store accepts no revision. A correction is a new filing, for example with the status SUPERSEDED or WITHDRAWN.

Once stored, the newest accepted text of every document in the law store is written into the bridge in one commit that stages only the files it wrote and names this filing as its cause, and the bridge is compared back against the store. Where the bridge cannot be written (missing, on another branch, detached, mid-rebase or mid-merge) the filing still stands, and the command reports it as not projected and exits 3; exit 2 means the comparison disagreed or an accepted text could not be written into the bridge. It does not update other clones.

saphan decision file <file>
flagtypedefaultwhat it does
--actorstringWho signs, required with --root-key and refused without it. It is never taken from your configuration or environment, because it is part of the signature; it is not the same as --filed-by.
--branchstringThe branch the bridge must already be on; checked, never switched to. Defaults to main.
--bridgestringAbsolute path to the bridge working tree; defaults to bridge inside the workspace.
--classstringThe document's mutability class: locked, team or open; required for a path-addressed canon key, refused for an architecture decision record. It is part of what is signed.
--decisionstringThe citation key this text is a version of, or a canon document's path in the bridge (required).
--envelopestringThe owner's signature envelope line, given directly; prefer --envelope-file, because a command line is saved in shell history.
--envelope-filestringAbsolute path to a file holding the signature envelope line.
--filed-atstringThe time of the filing, as an RFC 3339 timestamp (required); it must match what was signed.
--filed-bystringWho performs the filing (required); it must match what was signed.
--gitstringThe git executable to use; defaults to git on your PATH.
--root-keystring⛔ Absolute path to the root private key on this machine, to sign and file in one pass. Always a path, never key contents or a key elsewhere; refused together with an envelope.
--signed-atstringThe time of signing, with --root-key (refused without it); defaults to --filed-at.
--statusstringPROPOSAL, RATIFIED, SUPERSEDED or WITHDRAWN (required); it must match what was signed.
--titlestringA human-readable label; it is not part of the address.

saphan decision ls

Lists the citation keys the law store holds at least one text for, with each key's family and the title of its newest text. It says nothing about whether a text binds (use saphan decision show for that), and a failed listing is reported as a failure, never as an empty store. Unsigned drafts are listed too, marked as unsigned and not binding.

saphan decision ls
flagtypedefaultwhat it does
--citedstringArrayA citation key to check: is it in the store, and does its current text bind? Repeat for several; a key the store does not hold makes the command fail. A key the store holds whose current text does not bind is reported as in-store not-binding and does not make the command fail.

saphan decision migrate

Signs and files every canon document under the bridge's governance/, conventions/ and skills/ directories into the law store in one act, then writes the record back into the bridge and compares the two. The whole batch is judged before anything is written: if any document would be refused, nothing is written unless you pass --skip-refused. Documents already current are skipped, and a document whose bytes differ from the stored text is refused rather than filed as a new version. The same owner and edition checks as saphan decision file apply to every document, and a filed text cannot be changed or removed afterwards. On an installation with a gateway, run it on the gateway host itself; elsewhere it is refused. Prefix with saphan dry-run to see the verdict for each document without writing.

saphan decision migrate
flagtypedefaultwhat it does
--actorstringWho signs (required); it is part of every signature and never taken from your configuration.
--branchstringThe branch the bridge must already be on; checked, never switched to. Defaults to main.
--bridgestringAbsolute path to the bridge working tree; defaults to bridge inside the workspace.
--class-from-tableboolfalseTake each document's class from the table saphan decision classes prints (required, because the class is part of what is signed).
--filed-atstringThe time of the filing, as an RFC 3339 timestamp (required).
--filed-bystringWho performs the filing (required).
--gitstringThe git executable to use; defaults to git on your PATH.
--root-keystring⛔ Absolute path to the root private key on this machine (required); a path, never key contents.
--signed-atstringThe time of signing; defaults to --filed-at.
--skip-refusedboolfalseFile every document that would be accepted and leave the refused ones out, instead of refusing to start.
--statusstringPROPOSAL, RATIFIED, SUPERSEDED or WITHDRAWN (required); only RATIFIED documents bind and are written into the bridge.

saphan decision project

Writes the newest accepted text of every document in the law store into the bridge, as one commit staging only the files it wrote, then compares the bridge back against the store. It is idempotent: with nothing to change it makes no commit. It never deletes a file, never creates a missing directory, never switches branch, and refuses a bridge that is detached or in the middle of a rebase or merge. Exit code 2 means the comparison found a disagreement; 3 means nothing could be compared.

saphan decision project
flagtypedefaultwhat it does
--branchstringThe branch the bridge must already be on; checked, never switched to. Defaults to main.
--bridgestringAbsolute path to the bridge working tree; defaults to bridge inside the workspace.
--gitstringThe git executable to use; defaults to git on your PATH.

saphan decision show

Reads one document from the law store by its citation key and says whether it binds. Each text is reported as verified, unsigned, unverifiable (signed, but this machine cannot tell by whom), absent or unmeasured (the store could not be reached); only a verified RATIFIED text binds.

saphan decision show adr-0001
saphan decision show <citation-key>
flagtypedefaultwhat it does
--allboolfalseShow every stored text of the document, newest first, not only the current one.

saphan decision sign

Signs one filing's subject with the root key on this machine and writes the signature envelope to a file; this is the only step that touches a private key. The envelope is never printed: standard output carries only a record of the act (who signed, the key id, the time and the payload digest). Status and filing time are checked before anything is signed. A key file whose permissions let other users read it is refused. Who may sign is not checked here: saphan decision file checks it when the envelope is filed.

saphan decision sign <file>
flagtypedefaultwhat it does
--actorstringWho signs (required). For every document except an architecture decision record, saphan decision file accepts the filing only if this is exactly (byte for byte) an owner named in the installation's root-signed deployment record; if no owner can be determined, the filing is refused. For an architecture decision record any signer using the root key is accepted. sign itself does not check this. Never taken from your configuration or environment.
--classstringThe document's mutability class: locked, team or open; required for a path-addressed canon key, refused for an architecture decision record.
--decisionstringThe citation key this text is a version of, or a canon document's path in the bridge (required).
--filed-atstringThe time of the filing, as an RFC 3339 timestamp (required); it is part of what is signed.
--filed-bystringWho will perform the filing (required); it is part of what is signed and is separate from who signs.
--outstringAbsolute path the envelope is written to (required); a relative path is refused, and so is a file that already exists. The file is created readable by you alone.
--root-keystring⛔ Absolute path to the root private key on this machine; defaults to trust/root.key under the installation's directory (~/.saphan/trust/root.key for a per-user installation, /opt/saphan/trust/root.key for a machine-wide one). A relative path is refused. Always a path, never key contents or a key on another machine.
--signed-atstringThe time of signing, as an RFC 3339 timestamp (required); the command reads no clock, because this time is part of the signature.
--statusstringPROPOSAL, RATIFIED, SUPERSEDED or WITHDRAWN (required); only RATIFIED binds.
--titlestringA human-readable label; it is not part of the address.

Global options apply to every command — see Every command.

On this page