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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Trinity

[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14772/badge)](https://www.bestpractices.dev/projects/14772)

A personal AI agent that runs on your own machine. It remembers you, learns procedures, acts
through tools under a permission gate, reaches you on whatever surface you are using, and does
not lose your work when something crashes.
Expand All @@ -16,6 +18,15 @@ authorization), and the first slice of M5b (070, the gateway core): 25 slices, e
merge commit and tagged `slice/NNN` (`git tag -l 'slice/*' | wc -l` → 25, on 2026-09-23). What
that means in practice:

**Assessed against a recognised baseline.** Trinity holds the
[OpenSSF Best Practices passing badge](https://www.bestpractices.dev/projects/14772), the Open
Source Security Foundation's published bar for basics, change control, reporting, quality,
security and analysis, and the prerequisite the Linux Foundation names for a project entering
Incubation. Every required criterion is met; three *suggested* ones are recorded as unmet rather
than stretched, because a sheet with nothing on it survives a spot check less well than one that
says where the gaps are. The [assurance posture](#assurance-posture) below states what the build
enforces and what enforces it.

- **Talks.** Streaming chat with any provider behind one behaviour (`Trinity.LLM`), switched by
configuration; the assistant's text is persisted as a draft every 500 ms or 2 KB while it
streams, so a crash mid-turn loses at most that much. Context compaction with lineage when a conversation outgrows the
Expand Down Expand Up @@ -169,6 +180,8 @@ rather than described by it.
| Approved cryptography | A dedicated CI leg builds from source and runs the cryptographic properties inside a FIPS-mode container, so statements about approved algorithms are measured on that leg rather than asserted. |
| Supply chain | Dependency and licence audits run on every commit; dependency versions are pinned in `VERSIONS.md` and verified against the lock file by the gate. |
| Provenance | Every commit carries a Developer Certificate of Origin sign-off, enforced by a hook and independently by CI. |
| Independent self-certification | The project holds the [OpenSSF Best Practices passing badge](https://www.bestpractices.dev/projects/14772), assessed against the Open Source Security Foundation's published criteria for basics, change control, reporting, quality, security and analysis. Three suggested criteria are recorded as unmet rather than stretched: semantic versioning, which begins at the first supported release, and two concerning dynamic analysis tooling, which the project does not yet run. |
| Argued, not asserted | `docs/10-assurance-case.md` decomposes the top-level security claim into ten claims, each with its argument, the evidence a reader can check, and the limit on what it covers; the assumptions the case rests on are named rather than implied. |
| Claim discipline | `docs/09-standards-register.md` records one row per control a regulated deployment might ask about, each with an evidence path and a status. No public claim about a regulation or requirement is made without a row there saying it is true. |

## Milestones
Expand Down Expand Up @@ -198,6 +211,7 @@ later without renumbering anything.
| `docs/` | Vision, architecture, tech stack, conventions, data model, risks, security model, standards register; packaging, the FIPS leg, backup and restore, performance measurements |
| `docs/mcp-server.md` | Connecting a client to Trinity's MCP server (Claude Code, VS Code, Codex, goose), stdio, approvals over the wire, the headless profile |
| `docs/adr/` | Architecture decision records. One is added whenever a decision changes |
| `docs/10-assurance-case.md` | The structured argument that the security claims hold, with the evidence for each and the assumptions and limits named |
| `docs/09-standards-register.md` | One row per control a regulated deployment may ask about, with its evidence path and status |
| `lib/`, `test/`, `config/` | The application |
| `src-tauri/` | The native desktop shell |
Expand Down
24 changes: 22 additions & 2 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,38 @@ else
# connection (run of 2026-09-22). Every test still shares its owner's single connection
# with every process it starts (shared mode), so 010's "writers queue on one connection"
# holds within a test; the second serves the boot-time check alone.
# `busy_timeout` raised above the shipped 5 s (config/config.exs), for the same reason the
# Postgres branch above raises `queue_target`: a writer queued behind another on purpose should
# wait, not fail. DBConnection's queue settings govern checkouts; SQLite's own lock contention
# is governed by this. Five seconds is right for a desktop application and short for a suite
# that deliberately runs fifty concurrent conversations (slice 070 AC7) on a loaded shared
# runner, where it produced an intermittent `Database busy` that never appeared locally, in a
# different test each time - the signature of contention rather than of a defect in whichever
# test lost. The shipped value is unchanged and is still guarded by
# test/trinity/repo_config_test.exs, which reads it from the file rather than from this
# override.
#
# Measured afterwards and recorded here rather than left implied: raising this did **not** stop
# the intermittent failure (run 35889922107 failed on the very commit that raised it, while the
# pull-request run on the same commit passed). SQLite returns SQLITE_BUSY immediately, without
# consulting the busy handler, when a connection holding a read transaction tries to upgrade to
# a write while another connection holds the write lock, because waiting there could deadlock.
# No timeout affects that path. The raise is kept because it does help ordinary lock waiting,
# but it is not the fix for what is recorded as R26 in docs/06-risk-register.md.
config :trinity, Trinity.Repo,
database: Path.expand("../trinity_test.db", __DIR__),
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: 2,
queue_target: 5_000,
queue_interval: 30_000
queue_interval: 30_000,
busy_timeout: 30_000

config :trinity, Trinity.Repo.Receipts,
database: Path.expand("../trinity_test_receipts.db", __DIR__),
pool: Ecto.Adapters.SQL.Sandbox,
queue_target: 5_000,
queue_interval: 30_000
queue_interval: 30_000,
busy_timeout: 30_000
end

# We don't run a server during test. If one is required,
Expand Down
1 change: 1 addition & 0 deletions docs/06-risk-register.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
| R24 | Fired 2026-09-20, in slice 013: neither precompiled `mdex_native` artifact loads in Burrito's musl ERTS (both need glibc's `libgcc_s`; `_dl_find_object: symbol not found`). Lifted the same day by the owner's decision: the linux package builds the NIF from source for musl with Zig as the linker (`scripts/zig-cc-musl`, config/config.exs, `rustler` at build time), measured on a fresh install of the packaged binary rendering the real provider's answer; the `--smoke` path now prints `TRINITY_SMOKE_MARKDOWN=ok` and exits 3 otherwise, and the `package` workflow reads that line on every target, so a dead NIF cannot pass again. Residual: macOS and Windows use the precompiled artifact, and the `package` workflow has yet to run green there since the slice/011 tag (an older failure at "Serves HTTP 200", now diagnosed by the step's log). The row above stands as written | none | none | none | none | 013 |
| R23 | EMA / ID-JAG is beta everywhere (vendors label it so); IETF draft still moving | Low (re-scoped 2026-09-22) | Med | Spec revision breaks the exchange at the external authorization server, which is where it happens: Trinity redeems no assertion since the owner's decision of 2026-09-22 (slice 062 NOTES) | The resource server validates whatever audience-bound token that server issues, so a draft revision is that server's upgrade, not Trinity's; the row stays until the deferral lifts | 062 |
| R25 | `glib` 0.18.5 carries GHSA-wrw7-89jp-8q8g (medium, unsoundness in `Iterator` and `DoubleEndedIterator` impls); first patched in 0.20.0 | Low | Med | The advisory is open against `src-tauri/Cargo.lock`; measured 2026-09-23 | It is transitive through the GTK 0.18 stack (`gtk` 0.18.2, `webkit2gtk` 2.0.2) that Tauri 2.11.5 requires on Linux, so it is fixed by a Tauri release built on gtk-rs 0.20 and not by a direct bump. **Scope:** the desktop shell only. The `headless` release is `steps: [:assemble]` with no Burrito and no `src-tauri`, so a server deployment does not ship this dependency. Owner: the maintainer. Lift condition: a Tauri release whose Linux stack resolves `glib` >= 0.20, adopted by the desktop shell work | 100 |
| R26 | An intermittent `Exqlite.Error: Database busy` fails the test suite on loaded CI runners, in a different test each time, always on a write to the main repository | Med | Low (test-only; no production path) | Failed the gate on pull requests 67, 70 and again on the commit that attempted to fix it; the same commit passed on its other run, so it is seed- and load-dependent. Not once reproduced locally across repeated runs | Ruled out: `busy_timeout`, raised from the shipped 5 s to 30 s in the suite, did not stop it. SQLite returns BUSY immediately, without the busy handler, when a read transaction upgrades to a write while another connection holds the write lock; no timeout affects that path. The suite runs two connections (one for the sandbox owner, one for Oban's boot check) and deliberately queues fifty concurrent writers at slice 070 AC7, which is the load that provokes it. Candidate fixes, not yet taken: drain the stress test's sessions before the suite continues; give that test its own tag and CI step; or remove the second connection's need. Owner: the maintainer. Lift condition: ten consecutive green CI runs of the full suite with the stress test in the default run | 070 |
3 changes: 3 additions & 0 deletions docs/07-security-model.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# 07: Security model

The structured argument that these mechanisms deliver what they claim, with the evidence for
each claim and the assumptions it rests on, is `docs/10-assurance-case.md`.

## Trust boundaries

```
Expand Down
14 changes: 14 additions & 0 deletions docs/09-standards-register.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ document, a PROOF.md line) or `none`; the status; who decides the status. Status
| Trinity issues no production authority | The production profile validates the external issuer's tokens and mints none; the personal profile's issuer refuses to start under an external authority adapter and its tokens are marked and refused in production | `test/trinity/mcp/auth/embedded_test.exs` (refused at boot; no key material in production), `test/trinity/mcp/auth/token_test.exs` (the mark refused) | tree property, held by tests since 2026-09-22 | owner |
| Enterprise Managed Authorization (ID-JAG) | Not in this tree: the external authorization server redeems the assertion (owner decision 2026-09-22, slice 062 NOTES "Deferred") | docs/08 row; slice 062 NOTES | `not claimed`; deferred with a lift condition | owner |

## Open source assurance

| Control | Where Trinity satisfies it | Evidence path | Status | Decider |
|---|---|---|---|---|
| Recognised open-source security baseline | The OpenSSF Best Practices criteria at the passing level: basics, change control, reporting, quality, security and analysis | https://www.bestpractices.dev/projects/14772 | **passing, self-certified 2026-09-23.** Three suggested criteria are recorded unmet rather than stretched: `version_semver`, `dynamic_analysis`, `dynamic_analysis_enable_assertions` | maintainer |
| Linux Foundation Incubation prerequisite | The same badge; LF Incubation requires it at passing, alongside documented technical governance and a README per repository | https://www.bestpractices.dev/projects/14772, `GOVERNANCE.md`, `MAINTAINERS.md` | badge held; neutral asset hosting is not yet in place and is a foundation-side step | owner |

**Time-bound answers in that self-certification.** Three of the answers were true on the date given
and are not permanent: `report_responses` and `enhancement_responses` ("no external reports received
yet"), `vulnerability_report_response` ("none in the last 6 months"), and
`vulnerabilities_fixed_60_days`, which depends on the age of the open `glib` advisory recorded as R25
in `docs/06-risk-register.md`. They are re-checked when the badge is revisited rather than assumed to
still hold.

## Supply chain

| Control | Where Trinity satisfies it | Evidence path | Status | Decider |
Expand Down
Loading
Loading