CRUMB a card from devarno-cloud

Ariel Baselines: Upstream-Only Writes, Allowlisted Reads

traceo intermediate 6 min read

ELI5

A baseline is a photograph of a set of documents, taken at a moment you want to prove later. Traceo used to let anyone take, edit and freeze photographs. Now the photographs are taken upstream, in a separate tool called ariel. Traceo only shows them, and only to customers on an approved list. One operation still crosses the line: an admin can copy a frozen photograph’s contents into their workspace as requirements.

Technical Deep Dive

Source: traceo-cat/traceo_mcp_server/traceo_mcp_server/ariel_integration/policy.py:1-130, ariel_integration/mcp_tools.py:36-430, ariel_integration/repositories/factory.py:27-53, migrations/008_reconcile_schema.sql:128-131 and :668-745; decision 0104 (accepted 2026-10-10) and finding 20261010-1536; traceo-cat origin/main 7d647ab, read 2026-10-25

Decision 0104 in one table

OperationThrough REST or MCPRule
create, add-CI, freeze, buildrefused for every caller, owner includedBASELINE_WRITE_DISABLED. Baselines enter only through ariel (I-1)
verify, diff, list, trace, validateallowed only if the caller is authenticated and their tenant is in BASELINE_READ_TENANT_IDSan unset or empty list means nobody reads, and a refused read looks exactly like a missing baseline
ariel_sync baseline→traceoadmin/owner only, never anonymous, never a machine identitychecked from the principal, independently of ENABLE_RBAC
ariel_sync traceo→baselinerefusedthat direction would be a baseline write

The operator chose “operator tenant only” for reads on 2026-10-10. The tenant comes from the workspace.tenant_id that AuthMiddleware resolves for each request (policy.py:89-128).

Why: the store was shared

Finding 20261010-1536 recorded that production traceo:mcp left TRACEO_STORAGE_BACKEND and ARIEL_BASELINES_PATH unset (read from the Railway project on 2026-10-10, not re-read for this card). With both unset, repositories/factory.py returns the default YAML-file BaselineManager over ./baselines (/app/baselines in the container). It is one shared directory with no owner field, so every tenant saw and could write every baseline. Signup had gone public that same day.

The Postgres ariel_* tables from 008 (ariel_baselines, ariel_configuration_items, ariel_traceo_references, ariel_sync_history, all with ENABLE + FORCE RLS) exist. They serve baselines only when TRACEO_STORAGE_BACKEND is postgres or postgres_yaml. The v1.0 card described those tables as the live store.

flowchart TD
ENV{"TRACEO_STORAGE_BACKEND"} -->|"unset (prod, per 0104)"| YAML["BaselineManager: YAML files under /app/baselines, shared by all tenants"]
ENV -->|"postgres / postgres_yaml"| PGR["PostgresBaselineRepository: ariel_* tables with RLS"]
YAML --> POL["policy.py: writes refused, reads allowlisted"]
PGR --> POL
ARIEL["ariel upstream (pinned dependency)"] -->|"authors and freezes"| YAML

The lifecycle still exists, upstream

baseline_status is draft, frozen, superseded, archived. The transitions happen in ariel, not through Traceo’s tools.

stateDiagram-v2
[*] --> draft : authored in ariel
draft --> frozen : frozen in ariel
frozen --> superseded : newer baseline names it as parent
frozen --> archived
draft --> archived
superseded --> archived
archived --> [*]
frozen --> frozen : ariel_sync baseline_to_traceo (copies into workspace, admin/owner)

Postgres shape (when that backend is selected)

erDiagram
WORKSPACE ||--o{ ARIEL_BASELINES : owns
ARIEL_BASELINES ||--o{ ARIEL_CONFIGURATION_ITEMS : contains
ARIEL_BASELINES ||--o{ ARIEL_TRACEO_REFERENCES : cites
ARIEL_BASELINES ||--o{ ARIEL_SYNC_HISTORY : logs

Key Terms

  • I-1 → Traceo’s invariant that baselines are authored upstream in ariel and arrive only through the pinned dependency.
  • BASELINE_READ_TENANT_IDS → the backend env allowlist of tenants that may read baselines. Unset means nobody.
  • BaselineManager → ariel’s file-backed baseline store, and the default when no storage backend is configured.
  • ariel_sync → copies a frozen baseline’s items into a workspace as requirements. It is the only baseline-adjacent write left, restricted to admin/owner.

Q&A

Q: Where is a baseline authored and frozen? A: Upstream, in ariel. Traceo’s create, add-CI, freeze and build tools return BASELINE_WRITE_DISABLED for everyone (decision 0104).

Q: Which store serves baselines in production by default, and why is that a tenancy problem? A: With TRACEO_STORAGE_BACKEND unset, it is the YAML-file BaselineManager over one shared directory with no owner field, which every tenant saw. That is why reads are now allowlisted per tenant and writes are refused outright.

Q: Who may read a baseline through Traceo? A: Authenticated callers whose tenant is listed in BASELINE_READ_TENANT_IDS. Anyone else gets an empty list or a 404, as if the baseline did not exist. A GHA OIDC machine principal can only reach GET /api/ariel/baselines/<id> and still faces the same allowlist.

Q: What does ariel_sync do, and who may run it? A: It pushes a frozen baseline’s contents into a workspace as requirements (baseline→traceo). Only authenticated admin or owner principals may run it, never a machine identity. The reverse direction is refused.

Examples

An owner on a tenant outside the allowlist calls ariel_baseline_list and gets an empty list. They call ariel_baseline_freeze("BL-3") and get BASELINE_WRITE_DISABLED. The operator’s tenant is on the list, so the operator can ariel_baseline_diff("BL-2","BL-3") and then ariel_sync BL-3 into their workspace to seed requirements from it.

neighbors on the map