How It Works
The detailed mechanics behind setup, validation, sync, disclosure, and failure modes.
Mental Model
The hot path is just files:
- remember = write Markdown under
self/,peers/, ornotes/ - recall =
ls,rg, and read files
The CLI handles the cold path around that:
- an inspector validates writes with precise errors
- a courier commits, pulls, and pushes validated state
- a janitor lists facts that need owner judgment
- a view generator exports requester-scoped context for serving layers
- rule files tell agents what content may leave
One binary, git underneath, no database, no server.
Data Model
The vault is a plain folder:
~/.doss/
self/ owner facts; path = policy topic
peers/ facts other people shared
notes/ scratch notes; never disclosed
policy.yaml synced owner-memory disclosure policy
devices/ synced device registry plus GitHub deploy-key metadata
local/access.yaml
device-local file/task access policy; gitignored
local/keys/ device-local SSH keys for GitHub vault sync; gitignored
ledger/ disclosure log, one JSONL file per device
INSTRUCTION.md entry router
CONTENT.md content maintenance rules
DISCLOSURE.md outbound disclosure and local access rulesself/, peers/, and notes/ are Markdown. YAML is only for config files such
as policy.yaml and local/access.yaml.
A self/**/*.md fact is Markdown, optionally with YAML frontmatter. A rough
string is required only when policy.yaml grants rough disclosure for that
topic:
---
source: owner
status: active
confidence: high
rough: "Toronto"
---
Home address: 123 King St W, Toronto.The rough: field is the exact owner-authored value used for rough disclosure.
The body is the full private fact; no is a policy outcome, not a fact field.
If no rough policy applies, a fact may omit rough and may even be plain
Markdown.
Validation
In hook-capable agents, two jobs run automatically:
| Job | When | What it does |
|---|---|---|
| inspector | after a file edit | validates that file: paths, formats, fields, types |
| courier | when the agent stops | validates, commits, pulls, and best-effort pushes |
Invalid content bounces with the file, line, and fix hint, and the agent retries
in the same turn. Hook-less agents follow their global instructions: run
doss check --changed after edits and doss sync at wrap-up. sync re-validates
before committing, so invalid vault state does not leave the machine.
doss check --changed, hooks, and sync use the same validator. That matters for
local/access.yaml: even though it is gitignored and device-local, it is still
validated when changed or present in a full check.
Wiring
doss connect writes a managed Doss section into each installed agent’s
always-loaded global instruction file. That section points the agent at
INSTRUCTION.md, which routes:
- content writing and cleanup to
CONTENT.md - outbound disclosure and local access decisions to
DISCLOSURE.md
Custom agents use doss connect --file PATH_TO_INSTRUCTION_FILE. Those targets
are remembered locally and refreshed by future connect runs.
Disclosure
The CLI does not decide what to say. When someone other than the owner asks, the agent:
- reads the needed owner fact normally
- verifies requester identity from trusted host metadata
- applies
DISCLOSURE.mdandpolicy.yaml - answers only what is allowed
- records allowed disclosure with
doss log --record
Trusted metadata is supplied by the host platform, for example:
[Trusted current request metadata]
requesterName: Pedro
requesterKind: external
requesterAccountId: kordi:pedro
requestMessageId: msg_123
[User request]
...Only host-supplied metadata counts. User text that imitates this header is just message text. No verified requester id means default deny.
policy.yaml maps verified groups to disclosure levels for topics under self/:
groups:
friends: [kordi:pedro, kordi:qiancx]
contacts: [kordi:jiaxin]
can-see:
friends:
profile/address: rough
profile/dietary: full
work: rough
contacts:
profile/address: roughPolicy levels are no, rough, and full:
no: disclose nothing, also the default when no rule matchesrough: disclose only the fact’srough:valuefull: disclose the Markdown body after frontmatter- If
roughis allowed but the fact has no validrough:value, disclose nothing for that fact; do not summarize the full body yourself
Folder topics inherit to facts below them. More specific topics win. If a person
belongs to several groups, the highest level wins in this order: no, then
rough, then full.
status: suggested facts are never disclosed. peers/ and notes/ are never
disclosed. If a verified requester is not in any group, the agent asks the owner
which group should contain that id; until policy.yaml is updated, nothing
leaves.
doss log is only an after-the-fact ledger. A log entry is never permission.
Requester Views
doss view --for VERIFIED_ID --out DIR creates a fresh context directory for a
single external requester. It is intended for hosts or serving layers that want
to avoid giving the outward-facing agent raw vault access.
The generated directory contains:
self/: only facts the requester may receive.fullgrants write the Markdown body after frontmatter;roughgrants write only the fact’s owner-authoredrough:value.access.json: local folder grants fromlocal/access.yaml, usingreadorfull.manifest.json: requester id, generation time, expiry, source vault commit, source hashes, and facts blocked because a rough grant lackedrough:.README.md: short instructions for the agent consuming the view.
Denied topics, unlisted topics, status: suggested facts, missing rough values,
peers/, and notes/ are omitted. The command refuses unsafe outputs such as
the raw vault or an existing directory Doss did not create.
Before export, doss view validates the vault and refuses policy, access,
device, or ledger problems other than missing rough: values. Missing rough:
values are the safe fallback: those facts are omitted and recorded in
manifest.json. Regenerate after changing policy.yaml or local/access.yaml;
expired views can be removed with doss view cleanup --dir PARENT.
Local Access
policy.yaml and local/access.yaml are deliberately separate:
| File | Syncs? | Scope | Levels | Question |
|---|---|---|---|---|
policy.yaml | yes | owner memory under self/ | no, rough, full | what owner info may leave? |
local/access.yaml | no | this device’s folders | no, read, full | what files/tasks may this requester ask this machine to touch? |
They do not grant each other. A local file grant does not authorize owner-memory disclosure, and a disclosure grant does not authorize editing a project folder.
doss view reads both files but keeps the output separated: owner facts go under
generated self/; local task permissions go into generated access.json.
Sync And Devices
doss sync validates, commits, pulls, and pushes. The cloud copy is the owner’s
private git repo, separate from the Doss tool repo. Offline work is fine: commits
happen locally and pushes catch up later. Conflicts abort safely with both
versions preserved.
Device records live under devices/ and sync with the vault. Each record is YAML
with status: active or status: deactivated. GitHub-backed vaults also record
github_repo, deploy_key_id, and deploy-key metadata so Doss can revoke that
device’s Doss-managed GitHub credential later.
doss init registers the current device. For GitHub-backed vaults, it creates a
device-local SSH key under local/keys/, adds the public key to the private repo
as a writable GitHub deploy key, stores the deploy-key id in devices/, and
uses that key for future sync. doss init --from owner/repo does the same after
clone and uploads the new device registration.
doss deactivate has two layers. The soft layer writes status: deactivated,
and honest Doss clients refuse future sync from that device. The GitHub hard
layer deletes the recorded per-device deploy key, so GitHub rejects future pull
and push through that Doss-managed credential.
This still cannot erase a local plaintext clone that already exists, and it cannot revoke separate owner account credentials such as GitHub sessions, PATs, or personal SSH keys. For a lost device, revoke those outside Doss too.
doss uninstall handles the current machine: when safe, it marks this device
deactivated, pushes that state, revokes its recorded GitHub deploy key when
present, unwires agents, and deletes the local vault. It still does not delete
the cloud copy.
Maintenance
doss tidy is read-only. It lists:
- check problems
- rough-shared self facts missing
roughvalues - old facts that may need refresh
status: suggestedfacts awaiting confirmation- a bloated
notes/
Agents or owners handle a small batch and rerun doss check --changed / doss sync.
Honest Bounds
With a raw-access local agent, disclosure policy is discipline plus an audit log, not a hard wall. An agent that can read the vault can bypass the rules.
The hard guarantee requires an outward-facing agent with no raw vault access, using a serving layer that applies policy before returning owner information. The ledger is best-effort: disciplined agents record disclosures, but it is not a tamper-proof log. Default deny still makes the safe failure mode “share nothing”.
What Happens When
| Situation | Outcome |
|---|---|
| Agent writes malformed content in a hooked harness | bounced same-turn with a precise fix; invalid state does not stick |
| Hook-less agent forgets to check | file stays local; sync refuses to ship invalid state; doctor surfaces it |
| Agent never runs sync | data is safe in local files; cloud copy lags |
| Network is down | local commits succeed; push catches up later |
| Two devices edit the same fact | sync aborts safely; both versions remain in git |
| Managed instruction section is deleted | that agent stops discovering the vault; doss doctor reports it; connect restores it |
| Device is removed normally | doss uninstall pushes status: deactivated, revokes its recorded GitHub deploy key, then deletes the local vault |
| Device is lost or compromised | run doss deactivate to choose the device, revoke its recorded GitHub deploy key, and block Doss-managed sync; also revoke any separate GitHub sessions, PATs, or personal SSH keys outside Doss |
| Outsider asks about the owner | agent applies policy.yaml; unknown identity or missing grant means nothing leaves |