Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions docs/regulated/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Trinity for a regulated lab: what this pack is, and what it is not

For an ISSM or an assessment team deciding whether Trinity can run inside an accredited boundary.
It is written to be checked, not believed. Every claim below names a path in this repository, and
anything without one says **NOT IN TREE**.

## Read this part first

**Trinity is customer-controlled software. It is not a cloud service and this project is not a CSP.**
You run the binary, on your host, in your boundary. There is no Trinity-operated control plane, no
Trinity tenancy, and no Trinity staff with access to your data. That is what makes the rest of this
pack short: most of the controls an assessor asks about are yours, because the software is yours to
run.

**Trinity is not FedRAMP authorized.** There is no FedRAMP package, no 3PAO assessment and no agency
ATO for this software. A FedRAMP authorization is a property of a service offering; this is not one.

**Trinity is not CMMC certified.** There is no C3PAO assessment of this project. CMMC certifies an
organization's handling of CUI, not a library or an application, so the certifiable thing is your
lab, not this repository.

**This pack is not a HIPAA attestation.** No SOC 2, no HITRUST, no third-party audit of any kind has
been performed on this software. Sudo Apt Holdings LLC is not a Business Associate by virtue of you
running this binary, and nothing here should be read as offering a BAA.

**NIST has not evaluated, certified or endorsed anything in this repository.** Where this pack cites
a CMVP certificate it is the certificate of an operating system's cryptographic module, named so you
can check it against your own running binary, and `docs/fips-leg.md` says in terms that Trinity does
not claim the binaries are the same.

## What the project does claim

Three things, each of which an assessor can verify from the tree rather than from this page.

1. **Every governed action is recorded in a hash-linked, signed chain**, and the chain can be
verified by a reader who does not trust the program that wrote it. The verifier is
`lib/trinity/receipts/verifier.ex`; the task is `lib/mix/tasks/trinity.receipts.verify.ex`;
a standalone script that needs no application is `bin/verify_receipt.exs`. See
`receipts-verify.md` in this directory.
2. **The set of code paths that can cause an effect is closed**, and that closure is asserted by a
census over the tree rather than by intent. The boundary is `lib/trinity/effects.ex`; the
published population is `docs/effects-catalog.md`.
3. **Standalone, Trinity decides, acts and records by itself**, which is a specific and limited kind
of evidence. `authorization-boundary.md` in this directory states the limit rather than leaving
it to be discovered.

## What this pack does not decide for you

Whether Trinity may process CUI or PHI in your boundary is your determination, not this project's.
`customer-responsibility-matrix.md` sets out which controls are yours, which are the software's, and
which belong to the host or the cloud account underneath it. `data-flow.md` states what may enter a
model prompt and what may not. Read both before the others.

## The files

| File | What it answers |
|---|---|
| `authorization-boundary.md` | What is inside the boundary, what is outside, and where the trust ends |
| `customer-responsibility-matrix.md` | Who owns each control: the software, you, or the host |
| `data-flow.md` | What may enter a prompt, and what must never leave the boundary |
| `incident.md` | Reporting clocks and who to contact |
| `receipts-verify.md` | How an assessor verifies the record without trusting the application |
| `crypto-inventory.md` | Every cryptographic capability, its module, and its certificate status |

## Status of this pack

This directory is documentation only. It adds no code, changes no behaviour, and asserts no control
that is not already in the tree. Where the tree does not implement something this pack names, the
row says **NOT IN TREE** rather than describing an intention.

This pack documents the gap; it does not close it.
103 changes: 103 additions & 0 deletions docs/regulated/authorization-boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Authorization boundary

What sits inside the accredited boundary when Trinity runs, what sits outside it, and the one thing
about the standalone configuration an assessor has to understand before anything else in this pack
is useful.

## The boxes

```
ACCREDITED BOUNDARY (yours)
+--------------------------------------------------------------+
| |
| [ Trinity process ] [ Receipts store ] |
| BEAM/OTP application hash-linked signed chain |
| permission gate, effect own SQLite file, own |
| membrane, session loop connection pool |
| |
| [ Customer files ] |
| the filesystem roots you |
| grant, and nothing else |
| |
+--------------------------------------------------------------+
| |
| prompt + completion | OIDC / OAuth 2.1
v v
[ Model endpoint ] [ Customer IdP ]
OUTSIDE the boundary OUTSIDE the boundary
```

| Box | Inside the boundary? | Path |
|---|---|---|
| Trinity process | Yes. Runs on your host, under your accounts | `lib/trinity/application.ex` |
| Receipts store | Yes. Its own database file, separate from the primary | `lib/trinity/repo/receipts.ex`, `lib/trinity/paths.ex` |
| Customer files | Yes. Only the roots you grant | `lib/trinity/tools/fs/` |
| Model endpoint | **No.** Whatever you point it at, wherever it runs | `config/llm.exs:14` (the model list) |
| Customer IdP | **No.** Yours, and Trinity is a resource server to it | `lib/trinity/mcp/auth/`, `config/runtime.exs:100` |

Trinity holds a single-instance lock on the data directory so two processes cannot share one store:
`lib/trinity/data_dir/lock.ex`.

## Standalone: judge, actor and scribe

In the default configuration (`TRINITY_AUTHORITY` unset, or `local`) Trinity decides whether an
effect may happen, performs it, and writes the record of having done so. All three roles are the
same process.

- The decision is `lib/trinity/permissions/`, through `lib/trinity/authority/local.ex`.
- The effect crosses one boundary, `lib/trinity/effects.ex`, which re-derives the fingerprint from
the arguments actually passed before admitting the call.
- The record is `lib/trinity/receipts/chain_writer.ex`, signed before the row is written.

**What that is worth.** It is strong evidence to a reader who accepts that the host was not
compromised: the chain is hash-linked and signed, the effect population is closed and published,
and the verifier runs without the application. It proves the software did what the record says.

**What it is not.** There is no separation of duties. A party who does not already trust the host
has only the host's word for it, because the same process made the decision and wrote the record of
the decision. `docs/10-assurance-case.md` states this, and criterion C6 there records that a signing
key in a file proves the records were not altered by anything lacking read access to that file, and
proves nothing about custody of the key.

**A signed receipt with an untrusted clock is weaker evidence than one with a trusted clock**, and
Trinity's clock is the host's own reading: `lib/trinity/receipts/clock.ex`. A deployment that needs
the time itself to be trustworthy supplies a trusted time source. `docs/09-standards-register.md`
carries that as a real-world dependency rather than a property of this tree.

## Regulated use requires an external authority

For a boundary where the decision must not be the machine's to make, Trinity supports pointing
`TRINITY_AUTHORITY` at an adapter that lives outside this repository. When one is in force, Trinity
keeps **no executor** for the effects that adapter governs, and that absence is asserted by a census
over the tree rather than by intent (`lib/trinity/authority.ex`, and the census recorded in slice
027's record).

**Stated precisely, because the loose version of this sentence is wrong.** The seam is in the tree:
`lib/trinity/authority.ex` is the behaviour, and `lib/trinity/authority/selection.ex` picks an
implementation once at boot from `TRINITY_AUTHORITY` (ADR-0010). **One implementation ships here,
and only one:** `Trinity.Authority.Local` (`lib/trinity/authority/local.ex`), which is the sole
module in the tree that calls a tool's `execute/2` for an effectful tool. Grepping the behaviour
resolves two files and the second is a test fixture (`test/support/authority/adapters.ex`).

**No adapter other than `Local` ships in this tree.** An external authority adapter is a module in
another repository, and ADR-0008 (`docs/adr/0008-authority-is-an-adapter.md`) states that no slice
in this repository builds one. Nothing in `mix.exs` or `mix.lock` depends on one; an assessor can
confirm that from the dependency list rather than from this page.

An assessor evaluating a regulated deployment is evaluating that adapter as well as this software,
and this pack says nothing about it.

## What crosses the boundary, and when

| Crossing | Direction | Gate | Path |
|---|---|---|---|
| Prompt and completion | Out, then in | Every turn | `lib/trinity/sessions/prompt.ex:45` |
| Tool effects on files | Inside only | Permission gate, then membrane | `lib/trinity/effects.ex` |
| Web fetch and search | Out | Permission gate | `lib/trinity/tools/` |
| MCP server requests | In | OAuth 2.1 resource server | `lib/trinity/mcp/auth.ex` |
| Receipt forwarding | Out, if configured | Optional authority callback | `lib/trinity/receipts/forwarder.ex` |

A witness or transparency service that would let a third party detect backdated forgery without
trusting the host is **NOT IN TREE**.
75 changes: 75 additions & 0 deletions docs/regulated/crypto-inventory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Cryptographic inventory

Every cryptographic capability Trinity uses, the module that calls it, and what is underneath.

**Read the certificate column carefully.** Trinity implements no cryptography of its own. Every
primitive is OTP's `:crypto`, which calls the OpenSSL the host provides. A CMVP certificate belongs
to that provider, on that host, not to this software. **Anything not established from a document is
`UNKNOWN` and stays `UNKNOWN`.**

## The table

| Capability | Trinity module | Underlying | CMVP cert | Notes |
|---|---|---|---|---|
| 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 |
| 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 |
| 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)` |
| Receipt hashing | `lib/trinity/receipts/envelope.ex:36` | `:crypto.hash(:sha256, …)` | **UNKNOWN** | Over the DSSE pre-authentication encoding, not the bare payload |
| 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 |
| 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` |
| 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 |
| 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 |
| Sub-key derivation | `lib/trinity/keys/local.ex:415` | `:crypto.mac(:hmac, :sha256, …)` | **UNKNOWN** | |
| Key id | `lib/trinity/keys/local.ex:226` | `:crypto.hash(:sha256, "trinity/key-id" <> root)` | Not applicable | An identifier, not a secret |
| 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 |
| Device identifier | `lib/trinity/receipts/clock.ex` | `:crypto.strong_rand_bytes/1` | Not applicable | Random, names nothing about the host |
| JWK thumbprint | `lib/trinity/receipts/signer.ex` | `:crypto.hash(:sha256, …)` | Not applicable | RFC 7638 |
| Canonical JSON | `lib/trinity/receipts/envelope.ex:15` | `Jcs.encode/1` | Not applicable | RFC 8785, not a cryptographic primitive |

## Why every certificate says UNKNOWN

Because the honest answer depends on the host, and this repository cannot know it.

`docs/fips-leg.md` is the only place a certificate number appears in this tree, and it appears with
a qualification that this inventory repeats rather than softens. From `docs/fips-leg.md:53-59`:

> Red Hat's page, https://access.redhat.com/compliance/fips, read 2026-09-20, lists for Red Hat
> Enterprise Linux 9.8 the module "OpenSSL", version `3.0.7-395c1a240fbfffd8`, CMVP certificate
> #4857, status "Active". The version string the image reports and the version string the page
> lists differ in their build hash. Trinity does not claim they are the same binary, and does not
> claim a validation. What the leg establishes is narrower and exact: the gate and the suite run
> with OTP in FIPS mode against the FIPS provider that this distribution ships and names, with the
> algorithms the mode removes listed below. Whether a deployment's provider is a validated one is
> that deployment's question, answered from the certificate and the running binary, not from this
> file.

So: **certificate #4857 is the certificate of a Red Hat module**, cited for your convenience in
checking your own host. It is not Trinity's certificate, it does not travel with this software, and
on a different distribution it is irrelevant.

## What the FIPS leg does establish

`docs/fips-leg.md`, and the `fips` job in `.github/workflows/gate.yml`:

- The full gate and test suite run with OTP in FIPS mode, on every push, in a container built from
`ci/fips/Containerfile`.
- The mode is entered by `ERL_AFLAGS="-crypto fips_mode true -eval application:load(crypto)"`.
- The algorithms the mode removes are listed in that document, and the signer selection responds:
Ed25519 reports unavailable in the mode (`lib/trinity/receipts/signer/ed25519.ex:14`) and P-384 is
selected instead.

That is a statement about behaviour under a mode. It is **not** a validation, and no part of this
repository should be cited as one.

## What an assessor should do with this page

1. Establish which OpenSSL provider your host actually loads, and its certificate, from the running
binary. Not from this file.
2. Decide whether Ed25519 is acceptable in your boundary. If it is not, run in FIPS mode and the
selection changes by itself.
3. Note that **key custody is not addressed by any row above.** The default is a 0600 file
(`lib/trinity/paths.ex`), the seam for a KMS or HSM exists (`lib/trinity/keys.ex`), and **no KMS
or HSM adapter is in the tree**.
4. Note that **ML-DSA-87 is never selected by default** and requires the linked OpenSSL to carry it.
Loading
Loading