Skip to content

Commit e330eea

Browse files
authored
Merge pull request #109 from ScriptKittyOS/docs/regulated-pack
docs: a regulated-lab pack under docs/regulated
2 parents 07ac0ef + 240f44b commit e330eea

7 files changed

Lines changed: 593 additions & 0 deletions

File tree

‎docs/regulated/README.md‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC -->
2+
<!-- SPDX-License-Identifier: Apache-2.0 -->
3+
# Trinity for a regulated lab: what this pack is, and what it is not
4+
5+
For an ISSM or an assessment team deciding whether Trinity can run inside an accredited boundary.
6+
It is written to be checked, not believed. Every claim below names a path in this repository, and
7+
anything without one says **NOT IN TREE**.
8+
9+
## Read this part first
10+
11+
**Trinity is customer-controlled software. It is not a cloud service and this project is not a CSP.**
12+
You run the binary, on your host, in your boundary. There is no Trinity-operated control plane, no
13+
Trinity tenancy, and no Trinity staff with access to your data. That is what makes the rest of this
14+
pack short: most of the controls an assessor asks about are yours, because the software is yours to
15+
run.
16+
17+
**Trinity is not FedRAMP authorized.** There is no FedRAMP package, no 3PAO assessment and no agency
18+
ATO for this software. A FedRAMP authorization is a property of a service offering; this is not one.
19+
20+
**Trinity is not CMMC certified.** There is no C3PAO assessment of this project. CMMC certifies an
21+
organization's handling of CUI, not a library or an application, so the certifiable thing is your
22+
lab, not this repository.
23+
24+
**This pack is not a HIPAA attestation.** No SOC 2, no HITRUST, no third-party audit of any kind has
25+
been performed on this software. Sudo Apt Holdings LLC is not a Business Associate by virtue of you
26+
running this binary, and nothing here should be read as offering a BAA.
27+
28+
**NIST has not evaluated, certified or endorsed anything in this repository.** Where this pack cites
29+
a CMVP certificate it is the certificate of an operating system's cryptographic module, named so you
30+
can check it against your own running binary, and `docs/fips-leg.md` says in terms that Trinity does
31+
not claim the binaries are the same.
32+
33+
## What the project does claim
34+
35+
Three things, each of which an assessor can verify from the tree rather than from this page.
36+
37+
1. **Every governed action is recorded in a hash-linked, signed chain**, and the chain can be
38+
verified by a reader who does not trust the program that wrote it. The verifier is
39+
`lib/trinity/receipts/verifier.ex`; the task is `lib/mix/tasks/trinity.receipts.verify.ex`;
40+
a standalone script that needs no application is `bin/verify_receipt.exs`. See
41+
`receipts-verify.md` in this directory.
42+
2. **The set of code paths that can cause an effect is closed**, and that closure is asserted by a
43+
census over the tree rather than by intent. The boundary is `lib/trinity/effects.ex`; the
44+
published population is `docs/effects-catalog.md`.
45+
3. **Standalone, Trinity decides, acts and records by itself**, which is a specific and limited kind
46+
of evidence. `authorization-boundary.md` in this directory states the limit rather than leaving
47+
it to be discovered.
48+
49+
## What this pack does not decide for you
50+
51+
Whether Trinity may process CUI or PHI in your boundary is your determination, not this project's.
52+
`customer-responsibility-matrix.md` sets out which controls are yours, which are the software's, and
53+
which belong to the host or the cloud account underneath it. `data-flow.md` states what may enter a
54+
model prompt and what may not. Read both before the others.
55+
56+
## The files
57+
58+
| File | What it answers |
59+
|---|---|
60+
| `authorization-boundary.md` | What is inside the boundary, what is outside, and where the trust ends |
61+
| `customer-responsibility-matrix.md` | Who owns each control: the software, you, or the host |
62+
| `data-flow.md` | What may enter a prompt, and what must never leave the boundary |
63+
| `incident.md` | Reporting clocks and who to contact |
64+
| `receipts-verify.md` | How an assessor verifies the record without trusting the application |
65+
| `crypto-inventory.md` | Every cryptographic capability, its module, and its certificate status |
66+
67+
## Status of this pack
68+
69+
This directory is documentation only. It adds no code, changes no behaviour, and asserts no control
70+
that is not already in the tree. Where the tree does not implement something this pack names, the
71+
row says **NOT IN TREE** rather than describing an intention.
72+
73+
This pack documents the gap; it does not close it.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC -->
2+
<!-- SPDX-License-Identifier: Apache-2.0 -->
3+
# Authorization boundary
4+
5+
What sits inside the accredited boundary when Trinity runs, what sits outside it, and the one thing
6+
about the standalone configuration an assessor has to understand before anything else in this pack
7+
is useful.
8+
9+
## The boxes
10+
11+
```
12+
ACCREDITED BOUNDARY (yours)
13+
+--------------------------------------------------------------+
14+
| |
15+
| [ Trinity process ] [ Receipts store ] |
16+
| BEAM/OTP application hash-linked signed chain |
17+
| permission gate, effect own SQLite file, own |
18+
| membrane, session loop connection pool |
19+
| |
20+
| [ Customer files ] |
21+
| the filesystem roots you |
22+
| grant, and nothing else |
23+
| |
24+
+--------------------------------------------------------------+
25+
| |
26+
| prompt + completion | OIDC / OAuth 2.1
27+
v v
28+
[ Model endpoint ] [ Customer IdP ]
29+
OUTSIDE the boundary OUTSIDE the boundary
30+
```
31+
32+
| Box | Inside the boundary? | Path |
33+
|---|---|---|
34+
| Trinity process | Yes. Runs on your host, under your accounts | `lib/trinity/application.ex` |
35+
| Receipts store | Yes. Its own database file, separate from the primary | `lib/trinity/repo/receipts.ex`, `lib/trinity/paths.ex` |
36+
| Customer files | Yes. Only the roots you grant | `lib/trinity/tools/fs/` |
37+
| Model endpoint | **No.** Whatever you point it at, wherever it runs | `config/llm.exs:14` (the model list) |
38+
| Customer IdP | **No.** Yours, and Trinity is a resource server to it | `lib/trinity/mcp/auth/`, `config/runtime.exs:100` |
39+
40+
Trinity holds a single-instance lock on the data directory so two processes cannot share one store:
41+
`lib/trinity/data_dir/lock.ex`.
42+
43+
## Standalone: judge, actor and scribe
44+
45+
In the default configuration (`TRINITY_AUTHORITY` unset, or `local`) Trinity decides whether an
46+
effect may happen, performs it, and writes the record of having done so. All three roles are the
47+
same process.
48+
49+
- The decision is `lib/trinity/permissions/`, through `lib/trinity/authority/local.ex`.
50+
- The effect crosses one boundary, `lib/trinity/effects.ex`, which re-derives the fingerprint from
51+
the arguments actually passed before admitting the call.
52+
- The record is `lib/trinity/receipts/chain_writer.ex`, signed before the row is written.
53+
54+
**What that is worth.** It is strong evidence to a reader who accepts that the host was not
55+
compromised: the chain is hash-linked and signed, the effect population is closed and published,
56+
and the verifier runs without the application. It proves the software did what the record says.
57+
58+
**What it is not.** There is no separation of duties. A party who does not already trust the host
59+
has only the host's word for it, because the same process made the decision and wrote the record of
60+
the decision. `docs/10-assurance-case.md` states this, and criterion C6 there records that a signing
61+
key in a file proves the records were not altered by anything lacking read access to that file, and
62+
proves nothing about custody of the key.
63+
64+
**A signed receipt with an untrusted clock is weaker evidence than one with a trusted clock**, and
65+
Trinity's clock is the host's own reading: `lib/trinity/receipts/clock.ex`. A deployment that needs
66+
the time itself to be trustworthy supplies a trusted time source. `docs/09-standards-register.md`
67+
carries that as a real-world dependency rather than a property of this tree.
68+
69+
## Regulated use requires an external authority
70+
71+
For a boundary where the decision must not be the machine's to make, Trinity supports pointing
72+
`TRINITY_AUTHORITY` at an adapter that lives outside this repository. When one is in force, Trinity
73+
keeps **no executor** for the effects that adapter governs, and that absence is asserted by a census
74+
over the tree rather than by intent (`lib/trinity/authority.ex`, and the census recorded in slice
75+
027's record).
76+
77+
**Stated precisely, because the loose version of this sentence is wrong.** The seam is in the tree:
78+
`lib/trinity/authority.ex` is the behaviour, and `lib/trinity/authority/selection.ex` picks an
79+
implementation once at boot from `TRINITY_AUTHORITY` (ADR-0010). **One implementation ships here,
80+
and only one:** `Trinity.Authority.Local` (`lib/trinity/authority/local.ex`), which is the sole
81+
module in the tree that calls a tool's `execute/2` for an effectful tool. Grepping the behaviour
82+
resolves two files and the second is a test fixture (`test/support/authority/adapters.ex`).
83+
84+
**No adapter other than `Local` ships in this tree.** An external authority adapter is a module in
85+
another repository, and ADR-0008 (`docs/adr/0008-authority-is-an-adapter.md`) states that no slice
86+
in this repository builds one. Nothing in `mix.exs` or `mix.lock` depends on one; an assessor can
87+
confirm that from the dependency list rather than from this page.
88+
89+
An assessor evaluating a regulated deployment is evaluating that adapter as well as this software,
90+
and this pack says nothing about it.
91+
92+
## What crosses the boundary, and when
93+
94+
| Crossing | Direction | Gate | Path |
95+
|---|---|---|---|
96+
| Prompt and completion | Out, then in | Every turn | `lib/trinity/sessions/prompt.ex:45` |
97+
| Tool effects on files | Inside only | Permission gate, then membrane | `lib/trinity/effects.ex` |
98+
| Web fetch and search | Out | Permission gate | `lib/trinity/tools/` |
99+
| MCP server requests | In | OAuth 2.1 resource server | `lib/trinity/mcp/auth.ex` |
100+
| Receipt forwarding | Out, if configured | Optional authority callback | `lib/trinity/receipts/forwarder.ex` |
101+
102+
A witness or transparency service that would let a third party detect backdated forgery without
103+
trusting the host is **NOT IN TREE**.

‎docs/regulated/crypto-inventory.md‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC -->
2+
<!-- SPDX-License-Identifier: Apache-2.0 -->
3+
# Cryptographic inventory
4+
5+
Every cryptographic capability Trinity uses, the module that calls it, and what is underneath.
6+
7+
**Read the certificate column carefully.** Trinity implements no cryptography of its own. Every
8+
primitive is OTP's `:crypto`, which calls the OpenSSL the host provides. A CMVP certificate belongs
9+
to that provider, on that host, not to this software. **Anything not established from a document is
10+
`UNKNOWN` and stays `UNKNOWN`.**
11+
12+
## The table
13+
14+
| Capability | Trinity module | Underlying | CMVP cert | Notes |
15+
|---|---|---|---|---|
16+
| Receipt signing, default | `lib/trinity/receipts/signer/ed25519.ex` | `:crypto.sign(:eddsa, :none, …, :ed25519)`, line 21 | **UNKNOWN** | Ed25519, RFC 8032. `available?/0` line 14 returns false when `:crypto.info_fips() == :enabled`, so it is not selected in FIPS mode |
17+
| Receipt signing, FIPS mode | `lib/trinity/receipts/signer/p384.ex` | `:crypto.sign(:ecdsa, :sha384, …, :secp384r1)`, line 25 | **UNKNOWN** | ECDSA P-384 with SHA-384. Selected when the mode is enabled |
18+
| Receipt signing, opt-in | `lib/trinity/receipts/signer/mldsa87.ex` | `:crypto` ML-DSA-87, FIPS 204 | **UNKNOWN** | Never the default. `available?/0` line 24 checks `:mldsa87 in :crypto.supports(:public_keys)` |
19+
| Receipt hashing | `lib/trinity/receipts/envelope.ex:36` | `:crypto.hash(:sha256, …)` | **UNKNOWN** | Over the DSSE pre-authentication encoding, not the bare payload |
20+
| Merkle tree over a chain | `lib/trinity/receipts/merkle.ex` | `:crypto.hash(:sha256, …)` | **UNKNOWN** | RFC 6962 hashing: distinct leaf and node prefixes, odd nodes promoted not duplicated |
21+
| Blob encryption at rest | `lib/trinity/vault.ex:180` | `:crypto.crypto_one_time_aead(:aes_256_gcm, …)` | **UNKNOWN** | Fresh 256-bit data key per blob, `docs/encryption-at-rest.md` |
22+
| Data key wrapping | `lib/trinity/keys/local.ex:179` | `:crypto.crypto_one_time_aead(:aes_256_gcm, …)` | **UNKNOWN** | The key encryption key wraps the data key beside the ciphertext |
23+
| Key derivation from a passphrase | `lib/trinity/keys/local.ex:280` | `:crypto.pbkdf2_hmac(:sha512, …)`, 600,000 iterations (line 57) | **UNKNOWN** | Only when `TRINITY_KEYS_PASSPHRASE` is set |
24+
| Sub-key derivation | `lib/trinity/keys/local.ex:415` | `:crypto.mac(:hmac, :sha256, …)` | **UNKNOWN** | |
25+
| Key id | `lib/trinity/keys/local.ex:226` | `:crypto.hash(:sha256, "trinity/key-id" <> root)` | Not applicable | An identifier, not a secret |
26+
| MCP request state envelope | `lib/trinity/mcp/server/envelope.ex:49` | `:crypto.crypto_one_time_aead(:aes_256_gcm, …)` | **UNKNOWN** | Slice 061, key in the keys directory |
27+
| Device identifier | `lib/trinity/receipts/clock.ex` | `:crypto.strong_rand_bytes/1` | Not applicable | Random, names nothing about the host |
28+
| JWK thumbprint | `lib/trinity/receipts/signer.ex` | `:crypto.hash(:sha256, …)` | Not applicable | RFC 7638 |
29+
| Canonical JSON | `lib/trinity/receipts/envelope.ex:15` | `Jcs.encode/1` | Not applicable | RFC 8785, not a cryptographic primitive |
30+
31+
## Why every certificate says UNKNOWN
32+
33+
Because the honest answer depends on the host, and this repository cannot know it.
34+
35+
`docs/fips-leg.md` is the only place a certificate number appears in this tree, and it appears with
36+
a qualification that this inventory repeats rather than softens. From `docs/fips-leg.md:53-59`:
37+
38+
> Red Hat's page, https://access.redhat.com/compliance/fips, read 2026-09-20, lists for Red Hat
39+
> Enterprise Linux 9.8 the module "OpenSSL", version `3.0.7-395c1a240fbfffd8`, CMVP certificate
40+
> #4857, status "Active". The version string the image reports and the version string the page
41+
> lists differ in their build hash. Trinity does not claim they are the same binary, and does not
42+
> claim a validation. What the leg establishes is narrower and exact: the gate and the suite run
43+
> with OTP in FIPS mode against the FIPS provider that this distribution ships and names, with the
44+
> algorithms the mode removes listed below. Whether a deployment's provider is a validated one is
45+
> that deployment's question, answered from the certificate and the running binary, not from this
46+
> file.
47+
48+
So: **certificate #4857 is the certificate of a Red Hat module**, cited for your convenience in
49+
checking your own host. It is not Trinity's certificate, it does not travel with this software, and
50+
on a different distribution it is irrelevant.
51+
52+
## What the FIPS leg does establish
53+
54+
`docs/fips-leg.md`, and the `fips` job in `.github/workflows/gate.yml`:
55+
56+
- The full gate and test suite run with OTP in FIPS mode, on every push, in a container built from
57+
`ci/fips/Containerfile`.
58+
- The mode is entered by `ERL_AFLAGS="-crypto fips_mode true -eval application:load(crypto)"`.
59+
- The algorithms the mode removes are listed in that document, and the signer selection responds:
60+
Ed25519 reports unavailable in the mode (`lib/trinity/receipts/signer/ed25519.ex:14`) and P-384 is
61+
selected instead.
62+
63+
That is a statement about behaviour under a mode. It is **not** a validation, and no part of this
64+
repository should be cited as one.
65+
66+
## What an assessor should do with this page
67+
68+
1. Establish which OpenSSL provider your host actually loads, and its certificate, from the running
69+
binary. Not from this file.
70+
2. Decide whether Ed25519 is acceptable in your boundary. If it is not, run in FIPS mode and the
71+
selection changes by itself.
72+
3. Note that **key custody is not addressed by any row above.** The default is a 0600 file
73+
(`lib/trinity/paths.ex`), the seam for a KMS or HSM exists (`lib/trinity/keys.ex`), and **no KMS
74+
or HSM adapter is in the tree**.
75+
4. Note that **ML-DSA-87 is never selected by default** and requires the linked OpenSSL to carry it.

0 commit comments

Comments
 (0)