TIER docs

Privacy

The production v2 upstream view (#1500) exposes only pinned destination identities, schedule and delivery state, a nameless pending snapshot/page summary and a token-stored indicator. It returns no token, token-file path or pending body. Its status probe changes no delivery state. Central erasure requires JSON media and an exact empty object through the same normalized administration boundary; malformed requests create no restore block. Revocation removes incomplete page bodies and internal staging metadata in the same transaction as its audit. Retained nameless receipts consume the aborted sequence and let a reissued route rebuild without claiming partial delivery. The complete descendant route stops at a revoked middle admission; accepted history and origin-qualified restore blocks survive reissue.

Frozen monthly rows (#1497)

The verifier checks frozen monthly contributions and their authorized projection. Changing live loss settings or scoring policy after close does not revise that proof. Retired tiersealedmanifest1 receipts return COULD NOT CHECK; no old seal body is treated as evidence for named monthly rows. Archived live raw-input auditing keeps its separate digest contract. Scores, comparison and manifest HTTP routes no longer read sealed report bodies. Synthetic period selectors use frozen monthly rows and their shared k floor; legacy sealer state cannot change archived raw-window reads. Prefold person-key sets and seal-body assembly are removed. Monthly cache levers use frozen inputs and the same full-tree decision as comparisons; counted people with spend and merged work must independently meet k on both cache-input sides. Archived raw verification retains its separate per-measure census.

A writable server freezes each elapsed UTC month after 24 hours of grace, in one transaction. The monthly person/model table stores dated group paths, spend, points, fractional outcomes and additive lever inputs. A separate monthly partition preserves signed paid amounts without assigning bills to models; erasure pools those amounts without identifying allocation evidence. A companion stores captured event/outcome inputs and price provenance for reproducibility; it contains no prompts, code or credential-derived material. Late capture remains in raw storage and cannot revise a closed report. Close metadata discloses source coverage/loss and retention; a freeze does not certify complete capture. New closes also freeze the dated import/restart loss flag. Earlier metadata that did not record it is identified as such, without inferring an all-clear from current settings.

Monthly closes also protect the raw attribution inputs: the closed UTC month and its 14-day half-open lookback block insert/merge-time fills, read-time recovery, token promotion and historical price repair/repricing. Imported spend uses the same population writer and guard. CI provenance repair leaves closed outcomes and quality unchanged. Existing raw-verification receipt bands remain protected until that legacy adapter is removed. These guards do not prevent late raw capture; closed reports keep their frozen evidence.

Root report status reads monthly close metadata without people or digests, checks root authority in the snapshot and requires audit admission before responding. Seven-day reporting summaries remain explicitly labelled live receipt/watch diagnostics; monthly rows do not establish today's freshness. They use the shared k/complement decision without tree dominance.

Close checks each identifier's dated placement before joining aliases to one person. Captured spend without a named subject uses its producer identifier and does not become census evidence. Group totals sum the frozen dated rows, including Unplaced. Named last-upload status reads receipt history independently of the figures window, including when the figures are frozen.

Monthly retention runs in every mode using the configured window (default 90 days, 60–365). It pools whole UTC months ending before that horizon; the partial month at the horizon remains named until its whole month expires. Settings preview counts named monthly rows and rolls the simulated pooling back.

Erasure and retention remove named monthly rows and their captured evidence, adding their amounts to one nameless remainder per origin/month/group path/model. A later erasure merges into that same remainder. People plus remainders still sum to group totals. Anonymous group census counts frozen at close preserve the historical k decision after names disappear; they contain no erased-person keys. Mapped aliases contribute no additional census or peer seats; their captured spend and points remain in the named figures. Nameless before/after digests and conserved group totals record each erasure or retention revision; old named receipts are not rewritten or reported as reproduced. These are consistency checks, not an attestation against an owner who can rewrite the database. Frozen rows and evidence are included in subject exports. Ordinary database compaction/backup limits still apply. Whole-month bounds apply only to remainders visible in the selected projection.

Name-free monthly comparison decisions depend on group totals and the frozen anonymous census. Person names and row order cannot change their sums, census assignment or complementary suppression. The comparison endpoint projects the same full-tree decision for every authorized key, refuses narrowing filters, and omits hidden row identities, raw digests, row counts and erasure revisions. The exact row-space test oracle checks that no linear combination of published rows recovers a sub-k group. This is a k/complementary-suppression guarantee; it does not promise differential privacy across months or conceal changes in published group totals. Coaching retains its separate 1–50% contribution guard; its frozen outcome pool is ordered by value before bootstrap so renaming peers cannot change a typical range.

All the data TIER collects stays within your organization.

Partial closed-month reads exclude remainders outside the authorized person or dated branch before checking bounds. A visible remainder requires whole UTC month bounds and otherwise returns HTTP 400. Personal keys cannot read either monthly cross-branch comparison form; root and manager node keys retain those reports.

Admin sign-in sessions (#1417)

Admin-web sessions live only in process memory. Sign-in creates no database row, file or durable audit entry. A random 256-bit session id is sent only in an HttpOnly, SameSite=Strict cookie; it is never logged or written to disk. Cookies use Secure and the __Host- prefix whenever the effective origin is HTTPS; on permitted plain-HTTP loopback they use neither Secure nor __Host-. Memory holds a hashed session id, the credential verifier, a separate CSRF token, the serving origin and creation/activity times. It never retains the bearer secret. Session data disappears on sign-out, expiry or restart; expired entries are reclaimed at sign-in. Limits are 60 minutes idle, 12 hours absolute, 16 sessions per credential (its oldest is evicted) and 4096 simultaneous sessions per process. Each authenticated request checks the current credential and holder, so revocation or a recorded departure ends the session on its next request. Read-only servers support sessions without database writes.

GET /api/v1/me exposes only the credential id, kind, capability, holder, node id/name/path/level name and available page names. It exposes no key secret, verifier, prefix or length. Every admin-web response is no-store. The full admin token cannot sign in; shared read-token dashboard behavior stays unchanged. See identity access for Origin, CSRF, cookie and HTTPS requirements.

The separate dashboard shell (/signin, #1437, plan P20) sends a sign-in key only in the POST's Authorization header and clears the password field as soon as the request starts. Its JavaScript never reads or writes cookies, localStorage or IndexedDB, and never places keys in URLs or history. Admin keys and CSRF tokens stay out of browser storage in this shell; the separate legacy dashboard keeps its pasted API token in sessionStorage under tier_token (code: internal/dashboard/assets/dashboard.js, load, fetchJSON). On a 401 identity response, the shell saves the requested path and query (without a fragment) in the tab's sessionStorage under tier_admin_return_to. The client accepts at most 2048 characters, starting with one /, with no backslashes or control characters. Sign-in sends the path for server validation; a successful identity reload clears it before returning. Sign-out clears it even if the request fails. This return path is the shell's only use of sessionStorage; import state stays out of it. See Identity and access for retry and fallback behavior. It learns identity and navigation only through /api/v1/me; the acting banner displays the returned holder, key kind and node path using text nodes. Identity and navigation clear when leaving the page. Pages awaiting their APIs show only their title, without sample people, keys or reports. Every page, including an auxiliary page, is gated by the identity response's page allowlist. The shell pages and /admin.js are absent (404) in synthetic demo and unsealed recompute modes. A failed identity read opens sign-in only on 401; other failures keep the form hidden and offer a session-check retry without sending a bearer key. Sign-out success or 401/403 clears local identity and navigation. Other sign-out failures retain the shell and its in-memory CSRF token for retry and show that sign-out did not complete.

The existing server-held session CSRF token is returned in an X-CSRF-Token header only on a successful cookie-authenticated /api/v1/me response. The shell holds it in page memory for sign-out; it is absent from bearer identity responses and refusals. All dashboard HTML and scripts, including the legacy dashboard, serve CSP, nosniff and no-store. The legacy read-token form and its storage behavior are unchanged.

The legacy DSAR export (GET /api/v1/developer/{id}/export) carries the opaque node_id on its org_hierarchy and hierarchy_membership rows. It also discloses the subject's org, division and team names in org_hierarchy, which the tree keeps current on every rename or move, and the node the person was first placed in as org_people.created_node_id.

Who counts toward k (D4)

A person counts once in a reporting window only with roster membership, no bot classification, positive eligible captured cost and a qualifying merged outcome on the dated path, and frozen named evidence from this server: bound_upload from their own personal/device credential, server_watch (--watch-as), or vendor_attributed carrying a valid root-authored mapping ID. Roster-login matches and legacy aliases alone never count. Census and coaching use the same evidence predicate, including the own-credential check. Many devices still count as one named person. Later alias edits cannot lend somebody else's evidence to a person.

Vendor evidence counts only when the mapping names the event's frozen subject, the event timestamp is at or after its effective date, the mapping is not voided, and the event precedes any End. Root creates mappings prospectively: the first eligible date is the next UTC midnight. Earlier events and old alias-stamped vendor rows never gain counting eligibility, even on replay. End keeps past evidence, including lagged events received after End with timestamps inside the mapping interval. Invalid vendor identities leave spend stored unbound. Void withdraws past counting and cannot be undone. Existing monthly closes retain their frozen census after either action. Census and coaching use this same predicate. Both pollers now pass transient identity for root-mapped usage. Reconciliation keeps ended partitions inside their interval; voided partitions target zero on replay within the lookback and spend returns to the legacy path. Erased mapping partitions leave their day incomplete without deltas or settlement, while later days still ingest.

Vendor IDs live in vendor_mappings and nullable named org audit detail, never on token events. Erasure hard-deletes the person's mappings and ended or voided mappings with the same vendor and vendor ID, including rows assigned to other people. Another person's active mapping survives. It scrubs named audit detail matching both vendor and vendor ID to NULL. Mappings follow roster retention: aged leavers lose their mappings, and mappings never keep a person live by themselves. DSAR exports include every mapping with the subject's vendor and vendor ID, including rows assigned to other people, with vendor, vendor user ID, developer, effective/creation dates, creator, End and Void dates. Raw vendor identity is transient during ingestion; only the opaque mapping ID is stored on an event. Exports omit a NULL vendor_mapping_id to preserve pre-mapping export shapes.

Created people who never upload, imported people without local bound evidence, federated people, shared/legacy uploads, manual /costs rows, and outcomes alone count zero. Their eligible amounts can remain in totals; they cannot fill k. This is a breaking change for shared-token teams. Issue claim codes so people can claim personal keys and create device keys. Startup and tierd doctor --db explain when missing bound evidence withholds every group. Monthly closes store the counted-person census with their frozen evidence. Schema 30 drops legacy seal reports and their fold/census versions; they are not retained reports and cannot be read or verified.

Credential-free bound_upload evidence from dev builds between 2026-10-24 02:42Z and 22:16Z counts zero; no release writes that state.

For a withheld node reporting summary, root sees the cause; every other principal sees “Withheld — ask your admin”, with counts omitted. A managing node can invent people and upload through their keys; k does not protect against that node.

Upload freshness

Authorized people and key inventories show dates and Fresh / Stale / Silent / Never chips. Fresh means less than 24 hours, Stale means 24 hours through exactly 7 days, Silent means more than 7 days, and Never means no bound receipt, including legacy rows with no credential ID. Server receipt time determines freshness. Personal keys see exact times on /api/v1/me and /api/v1/me/devices. Ancestors see UTC dates only. People, device and self views preserve Silent and the last upload date or time after retention using persisted upload-credential receipt metadata older than the retention watermark. Shared upload and read tokens receive no freshness fields. The subtree reporting summary counts distinct active people reporting within the inclusive last 7 days; groups without k qualifying non-bot people in that window omit counts. Upload evidence uses one precise server receipt-time comparison for both eligibility and reporting, including fractional seconds. The existing tree complementary suppression gates the full forest before any subtree is selected, so parent and child counts cannot expose a sub-k sibling. All reporting-summary keys retain the shared floor of at least 3. All reads use org authorization.

Coach the practice, not the person. Grade the work, not the worker.

TIER helps engineers choose the right model, give it the right context, and spend where it pays. On real servers, personal keys can read their own figures and cards. Root and node keys can read named figures in their dated branch when the organization's names setting permits it. Cards clip events and outcomes to that dated branch and log each real open before construction. Personal keys read only their holder. My card shows the actual card and the person's own view log. An empty card helps a new install get started. "Your organization chooses who can expand groups to names. You can always see your own figures." TIER is built for coaching, not for evaluating developers. Per-person figures are not accurate enough to judge an individual. Anyone with host access can read the database; the API enforces these key scopes.

TIER is built so that adopting it does not mean handing over your source code or your prompts. This document states exactly what TIER reads, what it stores, and what it never touches — each claim grounded in the code so a security reviewer can audit it.

JSONL token events store a branch only as nullable branch_hash, a pseudonymised SHA-256 of canonical repo + "\x00" + full branch name with refs/heads/ removed. Raw branch names are never stored in token_events. Local watcher checkpoints keep the raw branch name in the plaintext JSON fields GitBranch and LastRealBranch. Default/unknown branches and unknown repositories produce NULL hashes. This is not anonymisation: guessable branch names can be reversed by hashing candidates with the known repo. Merged-PR outcomes store the same pseudonymised hash as head_branch_hash, never raw head.ref, plus nullable PR created_at and merged_at. Webhooks and backfill use head refs when available; API outcomes lack head refs and leave the hash NULL. Main, master, HEAD and a payload-supplied repository default branch have NULL hashes. Recognised revert exclusions retain the matching raw head ref or quoted title as audit evidence in outcome_exclusions, under the PR author's access and erasure scope.

Branch credit fires only for still-unattributed events in the same repo and head-branch hash inside [max(previous merge on that repo and head branch, PR opened − 7 days), merge]. It never changes existing attribution or closed monthly contributions; overlapping recorded claims are skipped. Reusing a branch bounds the next PR's credit at the previous merge; unrecorded merges cannot be considered. Missing head/open/merge evidence disables branch credit. The hash does not conceal guessable names. Existing webhook audit payloads retain their original body under the separate audit retention policy below.

The core guarantee

TIER never stores prompt or completion content, and never sends it anywhere you have not already sent it. Every capture path parses with an allowlist. The Claude Code JSONL parser deserializes only type, timestamp, gitBranch, cwd, sessionId, and message.{id,model,role,usage.*} — token counts, not text; the reverse proxy passes your prompts on to the provider you chose and reads each response as it passes back to extract id/model/usage, which is all it keeps; the Codex rollout-log parser reads six fields, none of which is content; the Opencode reader touches one table (message) and six fields inside its JSON blob, never the tables holding prompts, tool output, or credentials; and the Muse Code reader decodes ids, timestamps, token counts, model names, the workspace root and branch records, never prompt, reply or tool text. One opt-in switch widens the Claude Code read: with worktree attribution on (--worktree-attribution, off by default), the parser also reads the file or folder paths in tool calls and uses the one that seven tools name, never the file's contents, the tool's other arguments, or a command line. See Worktree attribution.

Proxy routes are mounted by default on a writable server. They forward traffic only when a client points at them; --read-only or an empty provider target removes the corresponding route.

Why the guarantee holds

TIER parses inputs with allowlist structs. Only the fields named in the struct are deserialized; every other JSON field — including any prompt or completion text — is silently dropped by the JSON decoder and never enters memory as a named value, let alone the database.

Because the parsers are allowlists, adding no new fields is the default — capturing content would require deliberately adding a content field, which does not exist.

Captured-dollar diagnostics

TIER denies GET /api/v1/diagnostics/captured-dollars on real servers. Doctor omits this check on HTTP 403 and instead reads frozen monthly close disclosures through the root-authorized diagnostic report manifest. The synthetic/offline diagnostic reads stored checkpoint cwd and session evidence plus read-only current git worktree metadata, without reopening transcripts. It emits counts and shares of captured dollars, never stored paths or session ids. Current worktrees cannot prove historical work locations. See the diagnostic limitations.

Worktree attribution (off by default)

Why it exists. Claude Code writes, on every line of a session file, the folder the session started in (cwd) and that folder's git branch (gitBranch). Many developers, and most coding agents, start Claude Code in the main checkout of a repository and then do the work in a git worktree: a second checkout of the same repository, on its own branch, in its own folder (made with git worktree add). The session file still says "main", so TIER books that spend to unattributed:main instead of to the issue the work was for. Worktree attribution (#823) finds the worktree the session actually worked in, from the paths its tool calls named. That needs a wider read of the session file than the token parser makes, so it is off unless you turn it on. It is an opt-in preview: its measured accuracy, and why a wrong attribution is permanent, are in how-it-works.md. How to turn it on, and the rules it applies, are in how-it-works.md. This section states what it reads and what it keeps.

Parents no longer carry (#1016); only message groups labelled isSidechain:true can use carry. Worktree attribution stays opt-in and off by default. Activation requires recorded gates, the operator's security rulings and an explicit operator decision. No new accuracy figures have been measured.

What it reads from the session file. A second allowlist struct (toolPathLine in internal/collector/toolpath.go), separate from the token parser's, decodes from each assistant line only:

It then keeps one path per tool call, for seven tools only: file_path for Read, Edit, MultiEdit and Write; notebook_path for NotebookEdit; path for Glob and Grep. Every other decoded value, including a path that an MCP tool or Bash carries, is discarded there, before any disk read, and never leaves memory. What the decoder skips is never copied: the text Edit removes and inserts, the content Write writes, the pattern Grep searches for, and Bash's command. A relative path, a network path (starting // or \\), a path over 4,096 bytes and a path containing a NUL byte are dropped; at most 16 distinct paths are kept from one line (maxToolPathsPerLine), from at most 64 content blocks (maxToolBlocksPerLine). It also reads two markers each line already carries, isSidechain and agentId, which tell a subagent's lines apart from its parent's (messageLineHead in internal/collector/messagemerge.go). Only isSidechain:true permits carry; an absent, false or wrongly typed label does not.

Which session files. tierd ship and tierd score decode the tool paths of every Claude Code session file whose session ended on or after --since, and look up the git files below for every path they keep, before they drop the sessions whose cwd is outside the repositories you named with --repo (parseSessionFileAttributed in internal/collector/jsonl.go). So the .git pointer files and worktree admin folders of other projects on the machine are read too, to recognise them as another repository's; nothing from a dropped session is stored. The live watcher also decodes the tool paths of every session file it reads, but looks up git files only for sessions whose cwd is in a watched repository.

Command lines are not read. Reading Bash commands (for cd <path> or git -C <path>) was considered and is not built. The operator ruled (via #826, recorded on #823) that it comes later, as its own change, behind its own switch, off by default.

What it reads from disk. For each path it keeps, and for the session's own cwd (rule 1 in how-it-works.md), it reads git's own bookkeeping files, never the file the path names, and it never runs git to do it (worktreeIndex in internal/collector/worktreeindex.go, branchAt in internal/collector/worktreereflog.go). TIER still runs git log on the repositories you configured, as it does with the switch off.

Paths under /tmp and ~/.claude are ignored.

What it stores.

What it never stores or sends. No tool-call path is written to an event or sent by tierd ship, and the code that reads and classifies the paths logs nothing. The dry-run audit (tierd score --worktree-attribution) prints separate tool-path and carry counts and USD costs, repository names, issue labels, session ids and times, never a path or any message content, and stores nothing. With the switch off, none of this is read and events carry no rule.

Messages held back until they finish (live watch mode only). One reply can be written as several lines of the session file, spread over minutes. With worktree attribution on, the live watcher (tierd serve --watch-repo) does not store a message until it has all of its lines, so each message is judged once, from every path it named. A message still open at the end of the file is held back: no event is stored for it meanwhile, though the file's checkpoint row (its path, and the session id) is saved. When the file has had no new line for 600 seconds (idleRelease in internal/collector/watcherattr.go), the next read releases it and stores it. The longest pause between two lines of one message was 417 seconds when this was measured (2026-09-30, over 156,834 multi-line messages in 8,532 session files on one machine), so the wait is longer than any measured pause. Idleness is judged by the file's modification time, and after a restart the watcher reads at once any file whose checkpoint stops short of its end. A known gap: if a line of a message arrives after that message was released, it is read as a fragment and stored again under the same message key. The store keeps the first issue, rule and price version. A coherent larger observation can promote token counts and recompute token-derived cost atomically at the original rates. Caller-authoritative cost, unavailable original rates, frozen input bands and incompatible observations prevent that promotion. Historical count/cost mismatches need explicit tierd doctor cost-consistency repair. A message held back when an erasure runs can be stored after it: see the erasure limits under Data-subject rights. tierd ship reads each file whole in one pass and holds nothing back.

Shipper credential and identity limits (#1144)

Use a per-developer upload credential with ship --developer maya --bound-token @/secure/maya-upload-token. The server stamps its subject and refuses a batch that names another developer. Shared ingest credentials are retired. Historical shared uploads remain in team totals but never count a person toward k or named coaching evidence. Root cannot upload through /events; only personal and upload keys can. Root can post a named manual cost through /costs, which supplies no counted identity.

A later cross-developer collision is rejected and reported as rejected_cross_developer in the response, with the original row unchanged. The server logs a WARN and the shipper prints the rejected count, without failing the ship or retrying. Same-developer replays still deduplicate, with a zero rejection count. Global idempotency stays unchanged; the guard cannot reverse a pre-claim that was inserted first.

What IS stored

TIER stores the following in a single local SQLite file. Configured network paths can transmit data; see Where it goes.

Schema 30 drops the obsolete local seal report, rollup, person, identity, coaching, node/fold/audit, gap, floor and canonical seal-history tables. Opening an older database does not convert those aggregate-only records to named months.

This list is not every table: the other per-developer tables (actual_spend, org_hierarchy, period_membership, quality_events, quality_history, repo_repair_audit, push_outcome_commits and push_outcome_audit) are in the erasure table under Data-subject rights below, which also covers the audit ledgers and the operator-identifying fields they carry (actor, reason, forgotten_by).

None of this is prompt text, completion text, code, file contents, or diffs, with these exceptions: free text in raw webhook payloads (for example PR titles, descriptions and commit messages) and the matching raw head ref or quoted title in outcome_exclusions are stored as written, with whatever was pasted into them. The session_id above is likewise an opaque UUID — it groups a session's events without persisting anything the session said or produced.

Server logs are a separate store. They can contain commit subjects, author logins, developer ids in request paths and client IP addresses. Commit subjects are free text and may contain whatever someone put there. Log retention and copies depend on the operator's logging setup; database erasure does not erase those logs.

Two of the entries above are conditional and hold data an auditor should note. An ordinary watcher tail-state row is derived operational state, safe to delete — the watcher rebuilds it from the live file on the next change. ⚠️ An erasure tombstone is not (see Data-subject rights below): deleting one while its session file still exists lets the watcher read that file from the start and store the erased session again. The tail-state contains no prompt or completion text and no file contents; its key is an install-secret HMAC of the path. Its cwd and (with worktree attribution on) its worktree path are absolute paths (filesystem locations, not the contents of any file), which can reveal an OS username or an internal project name. Raw webhook payloads hold third-party personal data (contributor names and emails) at rest, bounded to ~90 days. Revert exclusion evidence retains a matching raw branch or title, which can also contain third-party identifiers; it persists until the author's erasure or database deletion. Operators should treat the local database file (mode 0600, single-tenant) accordingly — and may decline the webhook path entirely to store none of the webhook PII.

Retention on writable servers (#826/#1371)

The retention engine and startup/daily scheduler run on every real writable server, including before the first watermark. There is no reporting-mode off switch. The view-log window is capped by the same named-history window, and the Settings preview simulates that same prune inside a rolled-back savepoint. Read-only serving and the private synthetic demo do not initiate age-prunes.

The default is 90 days, the minimum 60 days, and the maximum 365 days. The minimum is derived from store.RevertObservationWindow, the longest finite quality-change window; a smaller setting is refused with that reason. The ceiling supports a full annual coaching cycle, not performance reviews. An explicit serve --developer-retention-days N, TIER_DEVELOPER_RETENTION_DAYS or developer_retention_days config key pins the window, in that order. Otherwise the root Settings page's saved value wins over the default. Bounds are validated before use; there is no off value. A page save that lowers the window requires a preview and explicit confirmation and takes effect at the next prune.

One transaction tombstones affected watcher sessions before deleting their cost rows, preserves cost horizons, folds already-priced costs, paid invoices and allocated outcome points into a permanent name-free roll-up, freezes connected correction chains, scrubs old endpoints, removes expired named rows and advances a permanent watermark. Any failure rolls all of these changes back. Costs age by event time, outcomes by merge time, quality rows with their outcome, and paid spend by the end of its billing period. Current hierarchy and open memberships stay; closed memberships age by their end. A leaver's aliases, canonical history and current hierarchy disappear after the last retained reference ages out. Small team/division labels fold into other; no person key enters the roll-up. Offline live-window totals can include that history without re-pricing. Real-server /scores reads frozen monthly person rows and conserved nameless remainders; it refuses arbitrary historical windows. Historical repo/individual reconstruction is unavailable.

Widening the window works forward only and never restores pruned data. Before the first prune, installation backfill remains available. Afterwards, every historical named writer respects the maximum cutoff ever applied, in every mode. /events returns rejected_before_watermark; ship prints that count and treats those rows as final. The report manifest and startup log disclose the name-free refusal count. Consented graft imports separately retain safe source name-free totals under their destination node.

Event 3 reviews have no age deadline on intact, unpruned data. After a prune freezes a connected chain, later reviews are recorded and return ErrCorrectionFrozen without moving any allocation. Old endpoints read aged out. Closed months pool removed named contributions into nameless remainders, remove their evidence and retain the frozen census and conserved totals. Raw webhook bodies never outlive the developer window (and retain their existing 90-day/50,000-row cap).

Checkpoint keys are HMACs of the path using the install secret; the migration re-keys existing rows atomically without changing offsets or head fingerprints. Pruned sessions retain a tombstone and the watcher skips appends, including after restart. Historical checkpoint metadata copied to a new filename is also refused and replaced by a tombstone, using last activity time (or the legacy start time) and retained session events. Sessions with retained activity keep capturing. After a successful startup filesystem walk, unresolved file and subagent checkpoint keys are deleted; other collectors' keys are preserved. The job checks the result of wal_checkpoint(TRUNCATE) and retries within a bounded deadline. If a reader prevents truncation, the committed prune remains successful and logs a warning to use tierd backup. Successful truncation removes old WAL page images; pages freed before secure deletion was enabled still require the tierd backup / VACUUM INTO procedure below.

Back up before the retention upgrade. For a deployment requiring a full annual coaching window, select 365 days. Its database must be backed up before upgrading. Use the installed pre-upgrade binary against the actual meter database, writing a new snapshot file, for example:

tierd backup --db /path/to/meter.db --out /path/to/backups/meter-before-retention.db

Keep that snapshot separately and set developer_retention_days: 365 (or --developer-retention-days 365) before the upgraded server's first start. The snapshot contains personal data and needs the same access controls as the live database. A backup is a separate copy: erasure of the live database does not erase it. Age-pruning is destructive; the permanent name-free roll-up cannot reconstruct individual records. A rollback needs the pre-upgrade database snapshot.

Data-subject rights: access and erasure (GDPR Art. 15 / Art. 17)

Because TIER stores personal data keyed to a named developer, it ships two bearer-gated (write/admin-scoped) admin endpoints so an operator can honour a data-subject access or erasure request without hand-editing the database (#184). Root data-subject exports remain available and audited under every organization names setting; erasure retains its existing root authority:

Known residual — webhook_payloads. The structured erasure above does not rewrite the raw GitHub webhook bodies retained in webhook_payloads (only present when the webhook path is enabled). As described in What IS stored above, those raw bodies may embed a contributor's name or email address. Surgically editing a gzipped raw audit body to redact one identifier is out of scope for the erasure endpoint; the mitigation is the retention bound — every such row is pruned at startup and then every 24 hours while the server runs, by a 90-day age cap and a 50,000-row cap (PruneWebhookPayloads), shortened by an applied developer retention cutoff, so any identifier embedded there ages out within ~90 days. An operator who must guarantee immediate removal of that residual must delete the affected webhook_payloads rows directly. Running TIER without the webhook path stops new payloads from being stored, but rows already stored stay until the retention bound removes them or someone deletes them. The bound still runs with the webhook path off: each time tierd opens the database, and every 24 hours while tierd serve runs (cmd/tierd/webhook_prune.go). org_actual_spend is org-level (no developer column) and holds no personal data, so it is correctly outside the erasure/export scope.

Deleted bytes can remain until the database is compacted. TIER uses SQLite's secure_delete, but erasure and checkpoints do not guarantee removal of every leftover byte in the database file. This does not make the erased person visible to TIER's readers or reports. The database and any remaining bytes stay within your organization.

An administrator who wants to compact it now can stop tierd serve and run:

sqlite3 "$HOME/.tier/tier.db" 'VACUUM;'

Use the database path passed to serve --db if it differs from this default, then restart serve. Compaction keeps the database's current records, including frozen monthly rows and nameless remainders. Copies and backups already made remain separate files.

Alternatively, create a compacted backup and replace the database:

  1. Stop tierd serve.
  2. As the user that runs TIER, run tierd backup --db <path> --out <new-path>, where <path> is the --db you give tierd serve (default ~/.tier/tier.db) and <new-path> does not exist yet. tierd backup writes the copy with VACUUM INTO, which rebuilds the file from its live rows only, so the copy has no free pages and no deleted rows.
  3. Move the old database file, and its -wal and -shm files if they exist, out of the directory. Rename the copy to the old database's name.
  4. Start tierd serve and confirm your data is there. Then delete the old files you moved aside: they still hold the deleted rows.

Where it goes

The tier-federation/v2 contract sends closed monthly people, group paths, integer measures, conservation controls and frozen census to the authenticated, host-pinned parent. Raw capture evidence, aliases, keys and child audit stay local. Identities remain origin-qualified, including equal displayed logins. Name-free readers apply k, equality and complementary suppression here.

Owning tombstones are authorized through the approved origin's pinned route. Central-root erasure and local retention pool names into conserved remainders, block restoration and scrub invalidated staging and outgoing bytes atomically. A relay's own privacy action produces a redacted projection. Content digests bind exact retries; there are no signing keys or signed controls. The v1 aggregate wire, bridge and pending storage are removed. An upgrade with accepted v1 history refuses before changing its schema, rows or version.

The database is a local file (SQLite). Outbound network traffic depends on the commands you run, the collectors you configure and the traffic you route:

TIER does not call home or send telemetry to the project maintainers. The configured connections above can send data to your servers, GitHub and AI vendors.

This document bounds what TIER stores as content. It does not resolve the labor-law question: per-developer cost attribution is personal data about a named individual, and in TIER's EU target markets (DE Betriebsrat co-determination, FR/NL works councils, GDPR Art. 22 profiling) measuring named individuals is co-determined or restricted. For deployment guidance — works-council prerequisites, DPIA pointers, and a team-only vs per-developer decision table by jurisdiction — see legal-and-privacy.md. (Positioning guidance, not legal advice.)

Corrective work evidence

correction_declarations, corrective_relations, and correction_audit are personal data covered by access/export and erasure. Each endpoint includes nullable declaration and outcome ids, developer login, canonical repository and PR number (repo#PR), merge SHA, and occurrence timestamps. Bounded supporting text, disposition and appeal reasons, audit reason text and reviewer rationale are subject-bearing content, bounded to 4,096 bytes per text field. Audit actor and role are self-asserted; they do not establish authenticated identity.

TIER denies GET /api/v1/corrections/{kind} on real servers, including root requests. The synthetic/offline raw contract describes paginated relations, assessments, allocations and audit evidence in JSON or CSV, including free text and actor/reviewer identities. Use authorized subject-access exports or the local correction CLI for retained evidence.

Data-subject access exports all stored columns for either endpoint, including shared declaration text and the counterparty's identifiers on shared relations. For a reviewer who is neither endpoint, access returns only audit rows where they are the actor and assessments where they are the reviewer; it does not return other developers' allocations, relations or declarations. Canonical repo/PR matching includes excluded outcomes under either their original developer or trusted author, even when no outcome row remains. Erasure resolves aliases, scrubs the subject's copied identifiers and indirect declaration links, and clears shared text and audit actors before deleting the subject's declarations and outcomes, including their declaration stats and recorded timestamps. Relations and audit rows remain without the erased endpoint; surviving evidence is retained. Evidence capture alone does not enable scoring. When the operator enables Event 3 scoring, resolved allocations affect live points as described below. Public GitHub can still link the surviving endpoint to the erased subject through its PR history; this residual linkage is accepted by design. Erasing a reviewer who is neither endpoint clears only matching actor/reviewer identity columns. It preserves roles, reasons, supporting/disposition text and reviewed sizing, and never freezes or reallocates the parties' allocations. Schema version 8 prevents older erasers leaving this data behind. Canonical declaration replays preserve the first evidence. A replay with an edited declaration state or parent set returns the existing declaration id and the distinct unchanged-ignored status (ErrCorrectionDeclarationIgnored) to the store caller; it leaves declarations, relations, and audit rows unchanged. An identical state and parent set is a successful no-op, including reordered parents. The identifier-free API diff-stat cutover marker remains: pre-upgrade all-zero API evidence is conservatively unavailable, including genuine zeros recorded by the pointer-aware version-7 API writer, because the marker cannot distinguish those from older omitted stats stored as zeros.

Event 3 also stores correction_assessments and correction_allocations as endpoint-linked personal evidence. Assessments include reviewed original/own and before/after cumulative sizes, comparison commit identities, bounded scope and rationale, reviewer identity and review time. Allocations retain raw size, own quality, share, final allocated points and marginal growth as distinct exact rational values, plus pending/provisional/disputed and ranking eligibility state. Export includes only rows where the subject's outcome (including aliases) is an endpoint or participant, the subject is the reviewer, or the relation is a direct subject relation. Export does not traverse the connected chain. Erasure deletes the subject's own assessment and allocation rows, scrubs comparison identities, scope, rationale, reviewer and review time from direct subject evidence, and freezes every connected surviving allocation after severing its relation and parent links. Reviews of unrelated survivors remain intact. Only survivors' numeric allocations remain unchanged; they cannot be upgraded or reallocated after any party is erased. Counts for these tables include rows deleted, scrubbed or frozen. These rows do not enable scoring: the stored policy defaults off. Closed months preserve frozen contributions and policy provenance; schema 30 removes the old seal bodies and digests.

See Enable Event 3 scoring for configuration, rollback and startup behavior.

Enabled evidence and disputed state

Enabling scoring uses the stored reviewed assessments and allocations: reviewed cumulative growth is separate from individual allocated share. A pure repair can receive points transferred from the original without adding growth. Eligible unconfirmed same-author pairs use a provisional churn/file-count approximation; unconfirmed cross-author declarations cannot debit the other author. These figures are evidence with stated limits, not independently verified scope.

Actor and role are self-asserted local CLI input, not authentication. A single-operator installation confirms its own pairs. Requiring an actor label different from the declarer is advisory; it does not prove a separate reviewer. A dispute or withdrawal after confirmation keeps the previous treatment until operator settlement. Disputed state remains visible in developer/operator views, authorized exports, the corrective digest and monthly closes. Real servers deny raw correction diagnostic routes; synthetic/offline anonymised raw views return not_shown. An operator who can read the database can read retained rows. Enablement, disputes, settlement and late evidence do not rewrite closed contributions; erasure and retention remove names and evidence while preserving totals.

See Event 3 coverage for recorded declarations and limits.

Identity credentials and view records (#1370)

Subject identifiers, credential labels and kinds, issue/revocation times, view timestamps and routes, and remap old/new values are personal data. identity_credentials, coaching_view_audit and identity_remap_audit join the developer PII registry. Erasure removes held keys, view records and historical identity references. Exports include their identity metadata; credential hashes and operator credential fingerprints are secret material and are excluded.

org_people holds the developer id, creation and departure times, created_node_id (the node where the person was created), and creator/departure actor ids. Developer erasure removes these rows and developer export includes them. On-roster people stay live; a person marked left ages out from the departure click recorded at the server clock. Org write actors accept only credential UUIDs or the host-cli and break-glass sentinels, keeping developer names out of immutable node-edge history.

Token events also store server-written provenance and the immutable named_subject binding. Both are exported, and erasure finds events by either raw developer or frozen subject. Existing rows remain legacy_unverified. The view log's --view-log-days retention (default 90, range 1–365) is capped at the developer retention window whenever developer retention/pruning is enabled. See developer access and upload identity.

Coaching card boundary (#1372)

TIER authorizes the holder and records the view before constructing a card from one read snapshot. Root sees retained subject data; node readers see only the subject's dated contributions within their branch, and personal keys see self. View-history entries follow the reader's permissions, so different holders can receive different card contents. No persistent copy of the card is created.

The route requires an authorized holder key and a successful view-log write. A person with no spend in the window receives the empty card state. Named score reads return this same card. For an open month, its spend is every event recorded under the person's named subject or under any identifier currently aliased to them (the developer name where no subject is bound), inside the reader's dated branch; a closed month shows the frozen monthly rows instead. This is wider than the census: the census and peer eligibility count only bound_upload with the subject's own local credential or server_watch under --watch-as, and vendor rows count toward them only with root-authored vendor mapping provenance. For the census and peer eligibility, remapping an alias cannot promote excluded rows; on the card, remapping an alias adds that identifier's earlier events. The excluded-spend share uses rows bound to the subject or unbound rows recorded under that exact identifier; it does not infer ownership of old aliases. Excluded dollars never leave this calculation. The current card stores no additional personal data.

The 50% attributed-spend floor is independent of capture fidelity. Below it, TIER, its CI and work-type/no-issue figures are withheld with neutral text and an information icon. Typical ranges pool outcome costs across at least k other eligible same-team/org contributors, each above the attribution floor; they reveal neither identities nor per-person point values. Bot and remapped duplicate identities cannot pad that floor. Evidence is PR and opaque session-id links only, without titles, prompts, code or session contents. Structured annotations are deferred (PR8); the notes section states that explicitly.

TIER runs raw-data retention at startup and daily on every real writable server: 90 days by default, configurable from 60 to 365 days. Read-only serving and the synthetic demo do not initiate pruning; widening the window cannot restore removed detail.

Org tree and append-only audit

org_levels, org_nodes and dated org_node_edges describe the organisation; org_people records named enrolments. org_hierarchy, hierarchy_membership and retention_rollup carry node IDs to retain placement. Tree nodes and edges are shared structure and remain after a person's erasure. Their labels are organisation metadata, so operators should avoid putting personal identifiers in node names.

org_audit separates nullable named fields (developer, actor_developer, subject_credential_id, node_name_chain, detail_named) from retained action, time, opaque actor credential ID, subject node ID, node ID chain and name-free detail. It stores no credential verifier, token hash, prefix or token length. New hierarchy membership writers also use opaque IDs or the host-cli / break-glass sentinels. Pre-existing fingerprint rows are not rewritten.

Audit rows cannot be deleted or rewritten. The only update is a named value becoming NULL. Erasure clears those fields in rows naming the subject, actor, or credential subject. Access exports include exactly those rows; a row not about the subject carries only the subject's own name. Age pruning uses each row's timestamp and the same watermark-clamped developer window, within the developer prune transaction; team/division servers also run an audit-only scrub when full history folding has never been armed. Names in recent rows survive until their own cutoff, even if their subject has no recent activity. Audit rows never feed the live person set used by retention. Widening a window cannot restore scrubbed names.

Who sees org people and keys (#1421–#1423)

Root credentials see the entire organisation tree, people and public credential inventory. Managing node credentials see names, membership, creation metadata, reporting dates and public credential metadata within their own subtree. Read-only node credentials see the tree within that subtree, but neither the people nor the key inventory. Ancestors see creation and departure as dates only. The live branch and card routes also use dated holder scope, so root and node keys can read the individual figures inside that branch. Read-only node keys use the live Team screen for people; their key inventory remains closed. Targets outside the branch return 404, the same as missing targets. A move of a visible node requires authority over both parents; missing that authority gives 403. Shared and legacy tokens cannot reach these routes. Developer identifiers are global, so POST /api/v1/org/people answering 409 tells a manager that the name exists somewhere in the organisation, even outside their branch.

Personal credentials see only their own devices and exact last-data timestamps; ancestors receive dates and freshness chips. No shared view receives these inventories. A secret appears only in its creation/reissue response, marked no-store. Lists contain opaque credential IDs, labels and lifecycle metadata, never secrets, hashes, prefixes or token lengths. Reissue closes the old key and creates the replacement atomically. Revocation retains device history and does not erase work or revoke sibling devices.

Every successful administrative write appends an audit row in the same transaction with the actor's opaque credential ID. Names, labels and level names use the nullable named fields; permanent detail contains only approved name-free values. The existing developer retention window scrubs audit names on real servers. A newly enrolled person is not_counted until eligible named evidence arrives. The creation cap (default 25 per UTC day across a credential's whole branch, --people-cap-per-day) limits padding, but a manager can still invent people and upload their data: a node can pad its own subtree. k does not protect that subtree against its own manager.

Personal-key custody and invitations (#1425)

Managers and root distribute one-time claim codes, not working personal keys. A manager who keeps a code could redeem it before the person does; that is detected, not prevented: the person then sees "already claimed", and root sees the reuse refusal and the redemption (audited with the claim-code actor). A code works only on POST /api/v1/claim, expires after 72 hours and can be consumed once. The person receives their personal key once on redemption and can later rotate it from /me/key/rotate. Root recovery keeps their existing key active until the new code is redeemed, then revokes every personal key of theirs and every device those keys minted. Managers can end personal access only by marking the person left. A stolen unused code can be redeemed by whoever holds it; secure hand-off is still required. An already-used-code refusal states its claim time and is recorded for root. Failed codes share the IP authentication lockout.

org_claims holds developer, node, issue/expiry/consumption/cancellation times, opaque claim id, creator credential id or host sentinel, kind and the SHA-256 code hash. Plain codes are never stored or listed. Subject-access exports include lifecycle metadata and viewer_notices, excluding code hashes as credential security material. Erasure deletes both tables' subject rows in the same transaction as other personal data. Retention deletes whole claim and notice rows by their creation timestamps under the developer window, including when only audit retention is armed; they never keep a subject live.

Claim issue, use, observed expiry and cancellation are audited and notified to the subject. D10 applies: developer, actor developer, subject credential id, node names and named detail age out; action, time, actor credential id, node ids and opaque claim id remain. No code or verifier enters audit or notices. Devices added by a manager/root show that creator's opaque credential id and developer id to the holder, who can revoke the device. Mark-as-left stores the stated leave date on the person's org_people row; it is exported with that row and erased with it.

Access notices and settings changes (#1440)

Declarations, viewer keys and declaration notices are removed. Named card reads use the person, root or a dated branch holder, including read-only node keys, commit their view log before construction, and clip figures to the dated branch. The organization can choose names on (default), top admin only, or names off in Settings. Names off hides named figures from every other reader, including root; top admin only permits root alone to expand groups. Monthly records still include people under every choice, and changes are root-only and audited. Hidden-name reads use the existing whole-company monthly k decision, with no person/model filters, hidden row hashes or extra branch totals. Live screens show an explicitly requested whole closed month or the latest closed month. Personal keys keep their own live figures and card. Root data-subject exports remain available and audited under every choice; finance read-back refuses when names are off. The choice controls readers; it does not constrain the database owner.

The card states: "Your organization chooses who can expand groups to names. You can always see your own figures."

Person and node moves notify subjects of effective holder-access changes. A holder with several keys receives the union of those keys' access. The notice and move commit together, and a non-root self-grant is refused. Claim/key notices remain personal data, covered by retention, DSAR and erasure. /me/notices and /me/audit return only the caller's records. /me/views is a read-only personal view history, available without qualifying spend; root and node keys cannot use it. Historical audit names age out under the same scrub policy. Erasure's permanent audit retains action, time and an opaque actor ID without the erased name.

Root settings saves record old/new validated values and the actor credential ID, never credential-derived material. Explicit host flags, environment variables or config keys pin settings; defaults do not. The unsaved retention control defaults to 90 days. Saved changes apply without restart; lowering retention requires a token-bound preview and confirmation. Pruning remains scheduled, so confirmation reports a pending fold. The preview runs the scheduled prune inside a rolled-back savepoint and reports its current deletions and scrub counts without changing data or the watermark. Data arriving before the scheduled prune can change these counts. Existing view-log retention is capped at the effective developer window. Widening cannot restore previously removed data. Read-only serving mounts no save route.

Legacy seals recorded live settings values and sources before schema 30 removed their storage. Their writer and replay adapter are removed. Current monthly closes freeze scoring and source provenance; later Settings changes do not reinterpret frozen rows. Shared-ingest settings and the daily request tally are removed in schema 30.

Consented graft snapshots and temporary previews

Source HTTP refusals disclose only the numeric status at the destination, never the source response body or headers. Validated numeric size/limit fields remain available for oversized-bundle refusals.

The Source export and Company import dashboard pages use the existing cookie session and CSRF checks. The source page exposes only the signed-in person's consent; root can issue a shown-once export key with its expiry. Hide, same-tab navigation and pagehide erase that key from the page. The company page clears the pasted export key immediately when it starts the preview request, even if the request fails. Neither page writes secrets, previews, personal mapping choices or the returned apply summary to browser storage, URLs or history. Late responses cannot restore those values after navigation. Every clash needs an explicit saved map/new choice and target before apply is enabled; matching IDs never preselect a merge. The summary shows the API's stored receipt and keeps withheld totals withheld. Choosing map sends only the preview's matched existing person; editing a new identity cannot redirect that mapping. A preview conflict clears the staged batch and clash controls before another pull. Handoff notices about imported people appear only when the stored summary reports named people.

A graft bundle contains named events and dated outcomes only for people with active consent on the source and accepted by the pinned v1 import policy. Whoever holds the person's personal key can give or withdraw consent; this is only as strong as claim-code custody. A root or managing node that redeems the person's onboarding claim code can obtain that key and consent with it. Each consent change writes a subject notice as well as an audit row. Consent covers the fixed event subject (the stored developer when no fixed subject exists) and exact outcome developer ID; aliases are not expanded into additional named histories. Departed holders are excluded from named export. The bundle projects an explicit field allowlist: credentials, viewer declarations, audit records, and view logs are excluded. Stored micro-dollar costs, price versions, pricing bases, and provenance travel with named events; the source generates deterministic keys for unkeyed events. The v1 digest covers the entire bundle except the digest field itself, using the same canonical JSON helper as the federation contract.

Bundle v1 exports jsonl, codex-rollout, opencode, and muse events only at realtime fidelity, with nonnegative tokens and list costs within the import ceiling (10,000,000,000 micro-dollars). Other source classes, incompatible fidelity, negative cost or tokens, over-ceiling events, and invalid transport figures stay on the source. Non-push outcomes without a PR number stay too; push outcomes may have a null PR. New capture sources require a contract bump to travel in bundles. Negative or over-ceiling name-free cost totals are omitted as unsafe, without adding non-consenters to any retained-row report. Finite negative outcome and name-free points keep their existing semantics.

A required digest-covered left_behind report aggregates only active, non-departed consenters' retained rows, without a per-person split. Its basis: "source-claimed" label distinguishes it from verified destination usage. Its fixed ordered classes are jsonl, codex-rollout, opencode, muse, proxy, anthropic-admin, openai-usage, cursor-admin, copilot-billing, api, other (unknown event sources), and outcomes (retained outcomes, zero cost). Each source_class carries a count and signed net_cost_micro, including zeros. Missing, null, malformed, or overflowing reports are refused, never read as zero. Preview and the stored apply receipt show the report and left_behind_rows, separate from destination retention skips. Count and cost sums are overflow-checked. The dashboard labels the retained-row total and class table as what the source reports in preview and completion, hides empty reports, and allows import. The report does not add destination spend or census evidence. Preview refuses unacceptable row claims before storage; apply also refuses forged or hand-built stages. The bundle's v1 change uses existing JSON storage without a database migration.

Consent responses, consent notices, and bundle_pulled records state: "Some rows stay on the source because bundle v1 exports only accepted events and outcomes." Imports are exempt from --people-cap-per-day because apply is root-only and audited; ordinary people creation still follows the cap.

Non-consenting people and departed consenters contribute only to safe name-free closed-month totals; departed consenters remain excluded from named rows and the retained-row report. These totals use validated frozen monthly rows, the stricter frozen/requested k (at least three), per-person points/cost gates and shared full-tree k/complement checks across all frozen additive measures. Frozen counted evidence supplies cover; financial cells never add contributors. Active named consenters are excluded once through frozen canonical and alias evidence. Open months have no total. Pooled, unrecorded or otherwise unproven residual populations produce an explicit eligibility_not_established monthly omission; unsupported transport figures produce transport_figures_not_supported. The required digest-covered name_free_omissions array carries only whole-month bounds and a reason, without identities, figures or counts. Preview, stored apply receipts and import screens preserve it. These generated totals retain the transport marker sealed:false; bundles claiming sealed:true are rejected. Later raw edits and raw pruning cannot change a closed-month total. Two pulls can still be differenced as consent changes; closed-month suppression does not provide protection against that cross-pull inference. Consenting people's figures are excluded from name-free totals, including rows retained by the import policy. Those rows remain in the source ledger and are accounted for by left_behind in the bundle; consenters' source event counts and net cost equal exported plus retained events. Pull does not delete or subtract source ledger rows.

Named exports write a subject-keyed bundle_pulled audit row for every exported person, visible in their /me/audit. The stored row keeps the destination label in named detail. A recognized export key sent over plain HTTP is consumed before refusal. HEAD and other non-GET methods return 405 without consuming the key.

For an import preview, the company server fetches from whatever HTTPS host root names in the source URL and sends the export key to that host. Choosing that host is a root-only trust decision; internal, loopback and link-local addresses are allowed. The URL and fetch limits do not restrict which addresses it can reach.

On the company server the preview holds the validated bundle for at most 24 hours of use. A background sweep removes expired bundle bytes and mappings within the next minute while the server is running, and startup also cleans expired stages. The export credential is never retained. A source response echoing that credential is refused. DSAR, erasure, and retention select held staged people and batch-person metadata by their explicitly mapped local identity, falling back to the source ID only when unmapped. Erasing or ageing out that local subject invalidates the whole staged bundle. A colliding source name neither receives the held mapping nor invalidates the mapped person's stage. After staging is cleared, source-named subjects can export source metadata without the mapping's target developer, and erase that metadata. Erasing a source-only name in an id_mapped audit row nulls only source_developer; the target retains their audit row. Consent and mappings participate in DSAR and erasure, and named consent/mapping rows do not keep an otherwise aged identity alive.

A copy already imported by another server remains that server's responsibility: erasure must be requested there too. Withdrawing consent or erasing the source after a pull does not retract a copy previously delivered to the company server. The admin-only private-address SSRF residual is described in Security.

Applying a graft is one transaction, including the company import_applied audit. Mapping decisions have company id_mapped audits, and delivery already has the source bundle_pulled audit. No callback credential is retained. An identical bundle has one effect even if previewed again. Reused event keys or UIDs are skipped only within the same mapped destination person. Equal content from another person retains its own row and spend. When no same-person duplicate exists, a transport key or UID owned by another person refuses the transaction. No person field is added to bundle events, and name-free totals stay name-free. Conflicting content is flagged in the apply receipt only when a stored fingerprint exists and differs. Native rows without stored fingerprints cannot be compared; existing events are never overwritten. Source pricing and provenance are kept, including an unknown price version of zero, without applying local prices. Apply refuses negative or over-ceiling list costs and capture-source or fidelity claims outside the contract-pinned v1 local-collector policy. Rows before the permanent retention watermark are skipped as final, counted in the receipt, and never recreated; later rows still apply successfully. Identical content staged as a new preview records an apply audit referring to the original batch. New previews record a replay identity that excludes bundle issuance time while leaving the wire digest unchanged. A fresh export of that same content returns the original batch identity and lock times after destination erasure and recreates no named rows. A fresh batch rechecks name-free totals against the current destination k and preserves sticky withholding in storage and its receipt. Exact retries of an applied batch retain their frozen receipt. Before returning a replay receipt, apply removes the erased person’s plaintext batch-person and mapping rows. Mapping to an erased target also leaves the source person behind with the nameless erasure report line, even after remapping. Older applied batches with cleared staging have only their old issuance-dependent digest, so this identity cannot be reconstructed from that digest. Retrying their original batch ID still returns its receipt. Presenting the exact original bundle also preserves the receipt and records a stable identity for later pulls; older pending previews acquire that identity when applied. Changed content is a new import, subject to the erased-person record below.

Imported event rows cannot establish local census evidence even when their source provenance says bound_upload or server_watch. New imported people count zero until an upload bound to their own credential on this server; local watcher activity alone does not bind them. Apply issues no keys or claim codes. Viewer declarations are removed. A mapped identity with local history but no org-person row stays native when registered at the destination. Imported spend stays in the Unknown identity bucket until that subject has qualifying local evidence, then joins the subject in live totals and future monthly populations. Imported rows never supply census evidence; existing monthly census and apply receipts remain frozen. Imported event provenance is included in the person's DSAR; erasure deletes their named events through the existing subject erasure path.

Company name-free totals remain separate from named histories and the local census, in external_totals at the destination node. They appear as Other in the apply summary. Each source cohort must independently meet the destination k, floored at three; otherwise figures are null and no count is disclosed. Re-imports persist the highest threshold and any withholding, so a lower k cannot release a previously withheld total. Small cohorts are not pooled to claim k. A changed or overlapping total from the same source is refused atomically rather than added twice. These are source-reported aggregates: the company cannot verify distinct people or dominance from the name-free data. Frozen financial, horizon, retention, capture-loss and pricing provenance accompany the residual total and its first stored evidence; source sealed and price-version metadata are retained; apply does not turn a generated snapshot total into an authoritative local seal.

Source event identity aliases are retained for deduplication, including identities whose content matched an existing event. They are exported only for their mapped subject, erased with that subject, and pruned with the canonical event or an aged subject. They never supply census evidence or keep an identity live for retention.

Federation acceptance and refusal records

Federated figures are reported by them, not re-checked here against child raw captures. Production v2 receives monthly people and model associations from approved origins on authenticated, pinned routes. (origin_id,person_id) keeps namesakes distinct. Readers see only their authorized dated branch or personal scope, with name policy, k, lever eligibility and audit admission before bytes. Staged snapshots remain invisible. Conserved cells equal retained named rows plus nameless remainders in every additive integer unit.

HTTPS is required by default before credential lookup, including loopback. Direct TLS or HTTPS asserted by a configured trusted proxy satisfies it; untrusted forwarded headers never do. Root can allow plain HTTP between servers in Settings. Names, figures and the federation key then travel unencrypted. The same organization choice governs this server’s outbound HTTP federation requests. A federation key writes only its approved admission and origin routes, reads only its own status and grants no report access. Hosts pin the destination URL, admission and receiving installation; bearers remain in 0600 files. There are no signing keys or signatures. Owning tombstones are enforced through the authenticated route. Revocation scrubs incomplete staging and stops future delivery while preserving accepted history and restore blocks. Rotation preserves namespace and receipts.

Owner erasure issues tombstones and pools every retained local month for each resolved owning person ID, including earlier closed alias lineage. A relay's own erasure sends only its redacted projection. Central-root erasure pools names locally, installs permanent blocks, scrubs pending bytes and records a nameless audit receipt. Increasing retention cannot restore names. Incoming blocked identities are consumed and counted in blocked_records; their names are removed before staging. Restart-safe digest progress validates the original snapshot bytes before publication, and immutable controls conserve their amounts in nameless remainders. A retention preview rolls back its privacy changes; confirmed settings govern the scheduled prune. Longer retention, restart and exact retries cannot undo the stored cutoff.

Refused pushes are recorded without the presented token or any value derived from it. Records contain UTC time, a source-address class (loopback, private, public or unknown), a fixed reason, and the authenticated server id when known. Raw addresses and body-claimed identities are not stored. Root alone sees source classes. A top-level node reader sees only identified servers in its branch, plus unassigned attempts and hourly reason counts. Other node keys cannot read this log. Detail is bounded to 90 days and 10,000 rows; overflow retains only hour, reason and count, for 90 days. Refusals buffer in bounded process memory and flush on the existing 60-second worker and shutdown. The worker prunes durable detail and hourly counts even with no new refusals. A crash can lose unflushed entries: normally the last minute, longer during flush failures. No refused caller triggers a database write, failed flushes retain a bounded retry batch, and read-only serving performs no refusal writes. These records never feed the local census or retention_live.

People-page handoff and card notices (#1449)

The People page displays a newly issued claim code or invited manager's node key only in its transient result. Hide, same-tab navigation, pagehide and session loss clear it; it is never placed in localStorage, sessionStorage, browser history or a URL. As with the Keys page, opening a link in a new tab leaves the original page's shown-once result visible until it is hidden or that page is left. Recovery controls are root-only and use the existing claim-code API; a root sees "code used by someone else" from existing claim-reuse audit rows. This is a reuse warning, not proof of the redeemer's identity.

The People page distinguishes the stated leave date from the server-click date when they differ. The input defaults to and permits dates through the UTC day. Membership and keys end at the click; the stated date changes no figures. Erasure requires root and an exact typed person ID, separately from Mark as left. My card renders the card and view history. Named cards use the reader's dated branch. Schema 30 deletes declaration notices; the retained notice API carries claim and access lifecycle notices without writing read markers.

Live vendor identity review

Only root can explicitly fetch the People page's unmapped vendor users. The server reads current Copilot billing seats and Cursor team members with the configured collector credentials, holds the lists only for that request, and joins them to existing mappings and exact roster/alias matches. Opening People does not fetch a list. No fetched unmapped login, email or vendor ID is written to a table, cache, log, audit detail, error text or metric label. The GET works without writes, including under read-only serving. Vendor failures return fixed messages and no stale list; CSV import remains available.

The browser keeps review data only in page memory, clears it on navigation or sign-out, and uses no persistent browser storage. Root acceptance or CSV import creates the existing developer-keyed vendor mappings and one audit row per pair; those records retain their existing erasure and access-export coverage. A suggestion alone creates no mapping and never makes earlier usage count.

Monthly score readers (#1499)

Real-server /scores, /scores/compare and non-month report-manifest requests now use the same monthly name-free projection as org comparisons. Root, node, Read-node and personal keys receive the full-tree k/complement decision at the close's frozen k. Shared, legacy-admin and viewer credentials are refused. The census counts qualified distinct people independently of model rows; live arrivals cannot enlarge a closed population. The old identity-total calculation, including declaration visibility and hidden-spend dominance, no longer serves these routes. Named branch reports and self-only cards retain their own scope.

The default is the latest monthly close; the default comparison uses its preceding UTC calendar month as well. Missing closes return 404. These reads refuse arbitrary windows and narrowing filters, disclose the real close time and frozen capture-loss flag, and admit the bounded audit tally before releasing figures. They disclose no hidden row hashes, row counts or erasure revision. The dashboard clears prior figures before requests and displays each month's same gated decision. Closed months and frozen contribution rows are the only stored reports. Schema 30 drops legacy seal storage; archived raw manifests can still be checked offline, while retired seal receipts return COULD NOT CHECK.

Monthly branch reports

A writable tierd serve closes due UTC months at startup and checks again every minute. A month becomes due 24 hours after its end. Reports, CSV, trends, comparisons and month pickers read frozen month_person rows. Named monthly reports/manifests and people CSV include authorized frozen paid cells once, including paid-only people and nameless remainders. Name-free comparison JSON omits hidden identities, fold membership and census counts; published financial amounts use the same full-tree decision as points and list cost. Monthly disclosures preserve close-time horizons, partial-input status and pricing provenance. Named diagnostics stay within the authorized population. Name-free diagnostic counts publish whenever the whole row is shown; the frozen counts survive pooling. Withheld whole rows publish no counts. Team and group views expand to the people allowed by the reader's key. Live branch views remain available before close. Late raw input does not rewrite a closed month. Read-only serving starts no close worker. Source coverage and known capture loss are frozen as evidence and disclosures; they do not require arming or skips. The seal command, preview, status, --seal-from, --report-grace, seal_from and report_grace settings have been removed. Nonempty TIER_SEAL_FROM and TIER_REPORT_GRACE settings fail startup explicitly.

Group reports and CSV use named dated-branch projections. Name-free comparison and peer views use frozen rows with k and complement suppression. Cost-only and pre-tree spend remain in the monthly totals; pre-tree spend stays unplaced. Federation delivers the same closed amounts through v2 records; name-free receiving readers retain cost-only and sub-k suppression. No legacy seal or federation bridge producer runs.

Central monthly readers sum authorized local and committed received person/model rows and nameless remainders. A root can drill through immutable admission mounts to remote divisions, teams and origin-qualified people. Node reads require the branch and selection on the same dated path, in that order; personal reads stay local regardless of remote namesakes. Named views do not apply k. Cross-branch name-free comparisons run one full-tree k/complement decision, count opaque origin/person identities once, retain frozen anonymous census and exclude remainders from current peer and lever eligibility.

An explicitly selected month uses only that month's committed rows. Missing origins and their available history are explicit; incomplete figures say reported subtotal. There is no common-month search or substitution of older figures. Offline group/person views and exports say reported through YYYY-MM-DD from the latest committed closed month, never from staged pages or delivery activity. A server-approved normalized HTTPS hostname supplies full-card links; rows cannot supply URLs, and no central credential is sent to a child.

tiermonthmanifest2 reproduces authorized received sums, model allocation and ratios, distinguishes local refolding from received/deidentified rows and does not claim to refold child raw captures or coaching/timestamp evidence. Local tiermonthmanifest1 verification remains. The live endpoint stays local. All monthly reads admit the in-memory audit tally before releasing bytes and fail closed on audit failure, without request-time database writes. Production v2 is active and the v1 wire, storage, pending paths and aggregate bridge are retired. Monthly screens clear prior figures before refresh and reject stale responses after a new month or privacy decision.

Reporting coverage is scoped to the root/node reader's subtree in every aggregation mode. Its native evidence is a valid own-credential bound-upload receipt, with the census's roster, alias and bot exclusions. Shared and imported-unbound uploads cannot make a local group reporting. Native groups below k or complementary-withheld show status only, including in CSV; even setup/stopped reasons are omitted. Shown groups may expose an aggregate UTC report date, receipt-day end boundary, next daily receipt date and setup/stopped reason after the full dated population passes the shared k/complement gate, without tree dominance. Each distinct reporter contributes at most one census seat across dated paths. Monthly details also require a shown month_person group from the full-tree comparison decision, with k at least the frozen floor; multiple model rows add no census seats. Months without receipts use that monthly gate. Missing closes and withheld groups remain status-only; seals cannot authorize detail. Live and monthly views share the same reason definition. Receipt dates and setup/status remain live diagnostics over retained raw inputs, labelled local_evidence: "live_bound_upload_receipts" in JSON and CSV and explained on the Coverage page. Late uploads and retention can change that diagnostic; closed monthly costs and points remain frozen. No native group publishes contributor counts, even above k. The N-of-M summary counts group statuses, never contributors. Server rows describe the configured reporter's schedule and acceptance state, not individual freshness. Coverage is a read snapshot with no request-time database writes. It reuses the report/totals D9 in-memory subtree tally and fail-closed behavior and is absent under --read-only. Unended monthly requests return 400. Live server Current uses the end boundary of the latest month whose end plus the 24-hour monthly-close grace has passed; a complete accepted horizon remains Current even when its last push is over seven days old.

The Organization page displays closed-month cells only. Withheld figures and counts stay “Withheld” for root, “Withheld — ask your admin” for other readers. Published children remain visible under a withheld parent; the server suppresses complements. Its renderer omits contributor columns and counts for read-only node principals, including comparison and fold rows. Where provided, the API’s “Reported” or “Not reported” status replaces withheld people counts. Unavailable and months without a close both display “not available”, clear the previous figures and CSV link, and never become zero. Comparison places stored months side by side without computing a multi-month total or changing suppression.

The native “Teams on this server” rollup uses published root cells once, including dated slices, and never adds descendants to their ancestors. A historical cell whose parent was redacted is not treated as an additional organization root. If any required cell is withheld, this subtotal is withheld. When the current subtree contains an opaque federated place, the native subtotal is structurally “not available”. The report API also provides separate native/federated rollup fields, as described above; this page uses its monthly-cell renderer and does not derive a native remainder by subtraction or invent federation totals. Federated cells show “Read only · managed on its own server.” without figures or counts. Unplaced appears only at the tree root, labelled “Unplaced” in a distinct flagged row without a team contributor count. Root readers default to the structural company root when there is one. Organization page state and responses are never written to browser storage, and stale requests cannot repaint after navigation or session clearing. The shared sign-in shell stores only a bounded return path in tab storage, as described in Identity and access; sign-out and denied pages clear it.

On real servers, /time-to-merge reads frozen monthly outcome timestamp evidence for holder keys in every mode. It projects the same full-tree k/complement decision as monthly scores, then requires k distinct eligible counted PR authors per statistic. Multiple model allocations cannot pad authors or the PR sample. Every sample, contributor and exclusion count is null, so subtracting a measured count from unit-weight points cannot expose an excluded person's work. No hidden group receives a metric row. Older closes without timestamp evidence and erased evidence produce explicit null statistics; raw backfills cannot revise a closed month's durations.

Monthly name-free cache levers use frozen token numerators and denominators, with k distinct eligible input contributors on both sides of the cache partition. They release no hidden group IDs or other practice ratios. Erased people cannot supply category seats. Named branch and personal reports retain their authorized practice ratios, people and CSV. Opening a front-page group goes through the holder-scoped Organization report at the same month; it grants no cross-branch named access. The front page uses that month for time-to-merge too. Manifest failures clear stale figures and start no fallback report request. Legacy seal headers, stall states and disclosures are no longer interpreted by the browser.

Child federation exports (P28 + P29)

The production worker exports closed local and active received origin/month projections. It preserves original identity, immutable controls and owning watermarks, relays owning tombstones first, and sends a conserved nameless remainder after local erasure. Missing closes claim no successful delivery. Reserved exact pages survive retries and restarts. A privacy epoch change scrubs invalidated bodies, reconciles/aborts the remote identity and rebuilds the permitted projection. A connection test reads only status. No central request fetches child data. Token replacement retains the existing audited 0600-file handling and discloses no credential-derived values.

Federation and coverage display (P31 + P33)

The Federation page labels outside totals “reported by them, not re-checked here.” A missing or unavailable closed month displays no monetary amount; withheld totals also show no amount. A Company totals 404 renders None for all three rollups and leaves the CSV unavailable. Refusal detail and hourly roll-ups are bounded separately; roll-ups retain their counts. These pages add no per-team PR or person counts for closed name-free months. Coverage groups below the privacy floor show only Reported or Not reported, with dates and other details withheld. Reporting-detail checks use distinct reporting people per dated cell as their figures, preserving complementary suppression without tree dominance. Safe siblings retain their detail; a small group's necessary complement stays protected. Monthly detail also requires a shown frozen month_person comparison group. CSV uses the existing API suppression rules. Live server status is separate from the historical monthly report, and revoked servers are excluded from the current server denominator while their accepted history is retained.

Federation allow/reissue responses reach only the transient secret result. Browser persistence, URL parameters, and inventory rows never receive the secret. The child token paste field uses autocomplete="one-time-code" outside the schedule form. Pasted child tokens clear after submission and on navigation; the existing host file service owns atomic 0600 replacement and re-reads the file for every push and connection test. The page displays only the host-pinned central address.

v0.7.0 organizations: who can do what

The credential scope map links the rules for creating people, issuing keys and reading reports. The people and key inventory boundary also states the limit that a node can pad its own subtree. Padded people with qualifying bound evidence also lend k to ancestor rows, so the figures of a neighbouring real sub-k group can publish in an ancestor or fold. Anyone who can read that ancestor and knows the invented amounts could subtract them to learn the remainder. Monthly comparisons retain k and complementary suppression without person-share or tree dominance gates (code: internal/scoring/treesuppress.go, SuppressTree). Read it with the claim-code custody limit: k is not proof that an administrator has enrolled distinct real people.

One server per developer. Choose one reporting server for each person's live data. This is a deployment rule, not a check across servers. Federation sends monthly people rows with origin-qualified identities, but central has no cross-origin identity binding. It cannot recognize the same person reported by two children, or by a child and its own local capture. Reporting that person's work through both paths can count it twice. See federation acceptance for why federated totals are reported by them, not re-checked here.

When moving history between installations, use the graft consent and explicit mapping rules. The graft snapshot boundary explains why withdrawing consent or erasing the source cannot retract a copy already delivered to another server.

The child upstream SSRF residual covers outbound requests to the host's configured central origin. For a separate deployment review, see Legal & Privacy. TIER is built for coaching, not for evaluating developers. Per-person figures are not accurate enough to judge an individual.

Erased people and later imports (#1491, ruling A⁗)

TIER keeps this record so a completed erasure stays effective. Your organisation decides the legal basis with its counsel.

On an erasure request, if any identifier has an import trace, TIER stores one unlinked keyed hash for each canonical ID, alias and mapped source name. A batch row mapped to someone else is not a trace. If erasure deletes a mapping row whose target is someone else, it also records that source name; a wrong exclusion is reported and reversible. Applied-batch plaintext source-name rows for the erased mapped person are deleted. Retention ageing makes no entry and never deletes entries. The record holds no figures, timestamp or link between identifiers and uses its own HMAC domain. Hashes never appear in API output, logs, audit rows, receipts, previews or data-subject exports. Those compliance artifacts state the record's existence in fixed wording, without checking membership or counting entries.

The record is not anonymous: anyone holding the database could test a guessed identifier. It matches on the identifier alone, so the same identifier from any source is excluded, including a different person. Preview and apply leave a match behind, rechecking inside the apply transaction, with this report line naming nobody: “One or more people were left behind so a completed erasure stays effective.” The bundle is not refused; lookup failure refuses the import. A wrongly excluded person has to come forward because the report names nobody; root can release them. A different (renamed) identifier evades the record. A person erased here with no import trace is not recorded, except this clash case, so a later first import can recreate them. A staged copy can remain for 24 hours.

Entries are kept until root clears them or the database is deleted, with no timer. A clear removes only the named identifier; a returning person with several identifiers needs one clear each. Root states either “new consent” or “a different person with that identifier” (the API value is different person with that identifier). The response is identical whether or not an entry existed. Each clear is audited without the identifier, hash or entry count. An audited clear-all states “our counsel refused this record”. There is no dashboard control. Nothing in the release waits for counsel, and no Article is cited as the record's legal basis. The secret and entries share the database file. Entries do not survive a restore from an older backup that predates them: restoring it loses newer entries and undoes those erasures. Re-run erasures after a restore.

See the root API commands for one-identifier clear and counsel-refusal clear-all.

Import recording warnings (EXTREME)

Each source pull on both servers, and each destination apply, records its own attempt before taking the writer lock. Cleanup retries on a detached context for up to five seconds without holding the import-holder mutex during database-lock retries. The last holder releases waiting captures after cleanup completes. Abandoned attempts and failed live captures set a warning in the existing settings table, with the earliest and latest UTC dates when events may have been lost. Only overlapping report periods carry data_quality.live_capture_may_be_lost; org reports, totals and compare retain the warning stored with each monthly close. Later losses do not change old closes. Read-only serving re-reads the warning on every report. Busy startup recovery warns with a sanitized error class and serving continues. Pending warnings retry and writable close flushes them. No clear command is needed for later periods.

The dashboard says: “TIER may have missed some AI costs in this period, because a server restart or an import interrupted recording.” At most 10,000 captures run after responses concurrently; above that cap the response waits for capture. The erase dialog and success receipt disclose the retained erased-import record. Consent alone on a source server is not an import trace for that record.

Uploads during an import can receive 503. tierd ship retries the unchanged batch for up to three minutes after the first 503, with bounded exponential backoff and Retry-After support, and reports when it waits or gives up. Other upload clients must retry themselves; proxy capture uses the separate import wait described above. Re-sending an upload deduplicates.

A fresh import replay applies normally after root releases an excluded identifier when the original receipt had an erasure notice and no current exclusion remains. Same-person identity deduplication still prevents duplicate spend. Outcome export follows canonical aliases, retaining work type and provenance; rejected aliased outcomes are counted as left behind. A changed overlapping name-free total keeps the first total, records that choice in the receipt, and permits named rows to import.

Personal/upload credentials refuse departed subjects, including aliases, both at issue and on subsequent use. Copilot leaves a day unchanged when alias spend could overlap a preserved missing-seat mapping partition. Corrections CLI list/show open read-only, without migrations or startup pruning. The browser asks before hiding a one-time key or code; anyone holding a claim code can redeem it.

Live branch and model association (#1495)

/api/v1/org/live returns only the named people and activity in the credential's dated branch when the organization's names choice permits it. Root includes all people when permitted; a personal key includes only its holder. When names are hidden, the route returns the shared closed-month group decision instead. The row's upload time comes from receipt history independently of the figures window. Spend and points reconcile with the selected group's person rows. The live read adds no audit write; Team-screen reads do not appear in the person’s viewed-by log. The card states this limit. Card reads retain their mandatory committed view log.

Model points are a cost-weighted association with merged outcomes. Attributable cost can precede the selected period; the spend column remains period-bound. A missing model stays in a labelled remainder. The published Model Report task figures are bundled, linked and shown in their own units. No report is fetched at runtime. Protected anonymous API and proxy access is denied on real servers. tierd demo alone serves anonymous synthetic data from its private, disposable database; offline recomputation mounts no listener.

Production federation sends frozen monthly people/model rows and conserved cells. Central applies the reader's k/scope/name policy, with name-free group views below the threshold and audit admission before read bytes. Late raw events do not revise closed rows; nameless remainders preserve erased amounts.

Live figures: identity, placement and limits (#1496)

Live Team, Person and Models reads join aliases to one canonical person after checking each raw identifier's dated placement. Spend uses the stored named subject when present and otherwise the producer's developer identifier, so shared uploads and upgraded history remain visible without gaining census eligibility. Personal keys open only their own card and figures. Last upload uses receipt history independently of the figures window, with the existing credential receipt fallback after retention; its visibility follows the holder's branch at receipt time.

Root totals include activity before placement. The live response's groups array contains visible node totals (node_id, parent_id, name, spend_usd, merged_points), plus unplaced:true and direct:true rows where needed. Pass a direct:true row's node_id as ?node=direct:<placement> to read only that placement's direct people, under the same dated branch authorization. Unplaced belongs only under the root. With independent company roots and no placement evidence, it stays in the forest remainder visible only to the global root credential; company node keys cannot infer its owner. Children plus direct and unplaced figures reconcile with each parent; the overlapping ancestor rows must not be added together. Organization uses one live response and loads closed monthly reports independently, so large trees do not spend one read per group.

Live figures and holder cards default to 30 days and refuse windows longer than 90 days with HTTP 400: choose a shorter period or use closed monthly reports for older months. This window cap is the simpler bound on live read cost. Recorded zero-cost models receive an equal model share of associated points; remaining points are divided among paid models by attributable cost. With only zero-cost models the split is equal. "No model recorded" has a null cost per point rather than implying free work. Card opens on read-only servers explain that the view log needs a writable server.

Retired evaluation_mode YAML and saved settings are ignored. They cannot relax live access or k; historical report replay retains its recorded labels.

Federation comparisons clear unsuppressed local totals before returning the full-tree monthly k decision. The reading server applies the maximum of its configured floor, received close floors and the hard minimum. Pinned name visibility applies inside each report snapshot. Erasure derives opaque identities for stored logins that cannot be sent on the wire; those logins and erased people pool into nameless remainders at later closes, preserving additive amounts and frozen census. Retention cutoffs still refuse expired local closes.

Restoring a server from backup

Federation delivery after restoring a server from backup needs a new admission. A resynchronization contract comes after v0.7.0.

Monthly publication residuals (#1508)

The organization owns these servers and their data. Monthly group publication uses k and complementary folds of at least two members, with all 44 additive slots checked for partitioning. Equality of figures does not veto a fold. Paid, realtime and work figures do not decide which group rows are shown. Nodes come from month activity; zero-figure, zero-census nodes are dropped even when seated or paid-only. Whole company stays in the decision.

These exact residuals are accepted. A person with $30 of a group's $100 paid can subtract their own amount to learn $70. A fold may publish 12 points or $60 paid that all belong to one sub-k member. A manager can subtract a known $70 branch from a $100 company total to recover $30 elsewhere. A leaver's removal between $100 and $80 snapshots can reveal $20. A revised row reading $120, was $100, reveals a $20 billing change. Paid outside shown groups of $100 can be the sum of a $60 idle engineering allocation and a $40 idle sales allocation. A first-ever late tier-one bill of $60 on a dropped node enters that company line. This is also why name-free groups exclude these allocations at every ancestor. They receive one identical amount-free note whenever the company line is nonzero. Named readers retain a person where they sat; their group paid can therefore differ from the name-free group, with that note explaining why.

No paid-carrier count or matched ratio is published. Partial coverage is a boolean on group paid and Spend Leverage. Whole-company unjoined counts publish whenever whole is shown. Work categories use one frozen safe Other work union, counting distinct counted people across its categories.

Every close freezes the billing basis, including aliases, dated duration weights, seat flags and labels, even before invoices arrive. It is part of month_close's personal-data inventory, subject export, erasure and retention. Billing revisions use a separate ledger-ID chain and frozen basis. Erased seats retain nameless allocations on their frozen paths. Off-basis late payers, including re-posts under erased identifiers, enter the company line. An erasure, including retention pooling, first reconciles pending ledger rows in its transaction, bypassing the daily limit; a failure explicitly reports that the whole erasure rolled back. Readers distinguish awaiting billing, recorded dates, revised dates and previous amounts. Recorded is not final while a prior closed month's source is still expected. Closes with no basis say billing not recorded at close and refuse late invoices.

Federation v2 carries the frozen paid partition, recording/allocation status, realtime numerator, billing reconciliation and its independent revision chain per origin and month. It retains quality, horizon, retention, capture-loss, price/rubric/build evidence, dated labels and selectors. Relay forwards each origin once; its own invoice never reallocates onto child contributions. Readers sum integer numerators and denominators before deriving ratios; unknown billing remains unknown. Local and received rows use one census-only publication decision, with no fold-equality check. Paid stays outside that decision, with nodeless paid on the name-free company line. Erasure, retention and replay preserve amounts without restoring names. Multi-origin JSON and CSV retain each origin's disclosures. The organization owns its data; unreleased databases have no compatibility adapter. Paid is schema step 31 and federation is step 32.

Name-free live reads require one exact closed UTC month and refuse explicit partial or multi-month bounds with HTTP 400. Whole-month dashboard selections use the committed report including received origins. Name-free origin disclosures retain frozen warnings while omitting origin identity and build inventory; partial-input status follows only selected origins.