The canon¶
The canon is the source of truth for declarations: teams and their responsibilities, hosts and their policies, agents, placements, review policies and the passkeys each human has enrolled. It is a set of git repositories in the OKF v0.2 format, federated according to OKF Federation. Any other tool (a project page, an issue tracker) is a view of the canon; a change made there becomes a proposal, never a second source.
ameesh reads only the YAML frontmatter of OKF files. Unknown keys are kept and ignored, so a canon can carry more than ameesh needs.
Where ameesh reads it¶
AMEESH_CANON (or canon in the configuration file) points to the root of an
OKF bundle. If a federation.yaml is present there, ameesh also follows the
federation members that are present locally (workspace_path). A host can
also read several canons (v1.4.0).
Only the approved source¶
ameesh does not read the working files of the clone. It reads the canon at
the merged revision of the canonical branch, through git objects
(git show <commit>:<path>), and records that commit with every value it
synchronises. The canonical revision is, in order:
AMEESH_CANON_REF(configcanon_ref), for exampleorigin/main;- else the root member's
refinfederation.yaml, asorigin/<ref>; - else
origin/main.
Uncommitted edits and unpushed local commits are reported, never used. A
directory that is not a git repository is read only with
AMEESH_CANON_UNTRUSTED=1 (tests, prototypes), and every finding is then
tagged as not approved.
Fail closed¶
Every ameesh canon sync records the canon's status for the host:
| Status | Meaning |
|---|---|
ok |
read and valid |
invalid |
read, but an error blocks the whole host (federation, unreadable profile card, Host card) |
unreadable |
root missing, broken repository, revision not found, non-git directory refused |
Agents governed by the canon are claimable only while their host's state is
ok; no recorded state means not claimable. Leases, sessions and turns
already running are never touched. Agents registered by hand (without a canon
reference) are unaffected.
Commands¶
ameesh canon check [--host H] [--json] [--canon DIR] [--ref REV] [--fetch] # exit 1 on any error
ameesh canon show [--json] … # cards read, and from which commit
ameesh canon sync [--host H] [--json] [--bootstrap-ref BRANCH] … # canon → registry of host H
canon check validates the cards (see
Members, agents, hosts, placements) and
prints the host's placements. canon sync writes only declarative columns
of the registry (harness, host, cwd, admitted hosts and tags,
priority, model, budget_usd, responsible, team, provider,
credential_mode, capabilities, canon_ref), never state columns (lease, session, status, spend, prompt). An
agent removed from the canon is set stopped once its current turn is over;
nothing is killed.
When a canon is configured, the runner runs canon sync at start-up and then
every AMEESH_CANON_SYNC_INTERVAL seconds (default 300). A failure never stops
the runner; it closes claims through the canon state.
The trusted branch for passkeys¶
canon sync also copies Member.authenticators into the database's trust
registry. The branch that is trusted for this never comes from the commit
being read (its own federation.yaml could declare itself canonical). It comes
from the host configuration (AMEESH_CANON_REF / canon_ref), else from the
last commit already applied, else, on the very first sync, from
--bootstrap-ref <branch> (logged, ignored afterwards). The commit read must
be reachable from the trusted remote-tracking branch and descend from the last
applied commit: a sync never goes backwards and never re-activates a removed
passkey.
Several canons on one host¶
Since v1.4.0 (design decision 0031), a host can read several canons at once,
for example its own organisation's canon and the canon of a partner, acme.
The host's configuration carries a list of canons; the first is the
default canon:
{
"canons": [
{"path": "~/canon", "ref": "origin/main"},
{"path": "~/development/acme/home", "ref": "origin/main"}
]
}
or AMEESH_CANONS=~/canon:~/development/acme/home. A single canon keeps
working: it is a list of one. See
Configuration.
- Identity. A canon is identified by the
idof itsfederation.yaml(else by its folder name, with acanon-id-missingfinding). Two configured canons with the same id are a configuration error. - References. Cards of the default canon keep the historical
member:path@shaformat; cards of another canon are prefixed with its id:<id>/<member>:path@sha(for exampleacme/home:ameesh/agents/docs-writer.md@…). - Bounded sync. Each
canon sync(and the runner, for each canon at every pass) reads, stops, removes and retires only the rows of its canon. Syncing one canon never stops the other canon's agents, and an error in one canon does not prevent syncing the others. - State per canon. The canon state is kept per (host, canon): an invalid or unreadable canon closes claims of its agents only (and of their ephemeral agents, which carry their creator's canon).
- Names are global, first declarer wins. An agent (or a work package)
already declared by another canon stays with it: the second canon gets a
canon-name-conflictfinding limited to that card, and writes nothing. To move an agent to another canon, remove its card from the old canon (the agent is stopped and its name freed), then declare it in the new one: the row changes canon and restarts, with its mailbox and history. - Humans are resolved in the agent's canon.
human:<id>resolves only among theMembercards of the agent's canon (or of the host's or work package's). A human who works in both canons has aMembercard in each. - Each canon describes the host it uses. An agent's admission is judged
with the
Hostcard of its canon (harnesses, providers, models, credential modes, working directories,max_agentscounted on that canon's admissions). A canon without aHostcard for the host runs nothing there. The physical limits the runner applies (policy.resources,max_agents) are the strictest across the host'sHostcards in the loaded canons;ameesh hostsshows where each limit comes from. - Authenticators per canon. Each canon syncs the passkeys of its own
Membercards, with its own journal, monotonicity and trusted branch (therefof itscanonsentry;canon_ref/AMEESH_CANON_REFonly for the default canon). The same passkey used in two canons has two entries, each updated and revoked by its own canon only. A receipt is verified only against the authenticators of the canon of the approved action. - Tools.
ameesh canon check|show|syncandameesh placement checkprocess every canon (one block each;{"canons": [...]}in JSON);--canon <id>selects one.ameesh agent spawnrefuses a name declared by any canon.
Constraints: every host that shares a database must have the same default canon; do not reorder the canons and upgrade in the same step; removing a canon from the list leaves its rows untouched (its agents are no longer claimable, nothing is stopped).
Bounding ameesh to a folder of a shared canon¶
A canon shared with other tools can contain files whose type matches the
ameesh profile without being ameesh cards (for example type: Agent files
that describe another tool's sub-agents). Read as ameesh cards, they would make
the whole canon invalid. federation.yaml can bound the ameesh cards
(Agent, Host, Placement, Member, WorkPackage) to one or more folders
relative to the bundle, under extensions, the only open key of the OKF
Federation schema:
id: acme
root: home
extensions:
ameesh:
scope: ameesh # or a list: [ameesh, teams/ops/ameesh]
members: # optional: other federation members
tools:
scope: ameesh
members:
- id: home
...
- With the key, only cards under the scope are read; files of the same
typeelsewhere are ignored, with one aggregatedameesh-scope-ignoredinformation per member.canon showprints the scope. - Without the key, nothing changes: the whole canon is read.
extensions.ameesh.scopeapplies to the root bundle; another member is bounded byextensions.ameesh.members.<id>.scope. The root manifest decides: a member does not bound itself.- The older form (
ameesh:at the top level,members[].ameesh) is still read, with the warningameesh-scope-legacy(it fails the OKF Federation validator);extensions.ameeshwins when both are present. - Fail closed: an unreadable scope (absolute,
.., pattern, hidden folder, empty list:ameesh-scope-invalid) or one missing from the revision read (ameesh-scope-missing) is an error: no card of that member is read, the canon is invalid for its agents, and nothing is stopped or retired. - Declaring a scope on a canon already synced retires the cards left outside it: move the cards under the scope in the same pull request as the key.
Next¶
- Members, agents, hosts, placements: the card types.
- Write a canon: a worked example.