Concepts
Seal and integrity
What makes a Tama hook release tamper-evident, and which checks actually stand between a modified tree and a running session? One digest definition, computed in one place, enforced at three moments.
#What it is
The seal is a SHA-256 digest over every file in the release tree except
release.json, in sorted path order. Per file, three length-framed values
enter the hash: the relative POSIX path, the permission bits
(mode & 0o777, 4 bytes big-endian), and the raw bytes. The definition
lives in exactly two places that must agree by construction:
tama hooks seal (writer) and
tama hooks install-release (enforcer); the three read-only
reporters import the installer's copy rather than restating the rule
(scripts).
Consequences that follow directly from the definition:
- a one-byte rewrite changes the identity (walkthrough);
- a
chmodwith identical bytes changes the identity (walkthrough); - adding or deleting any file changes the identity;
- editing
release.jsonitself does not — the manifest is the claim, the tree is the evidence.
#Who declares it
tama hooks seal, run once by Scripts/build-app.sh after the
release tree is staged and pruned. The digest becomes releaseId in
release.json (schema ai.wisent.tama.hook-release.v1), beside
packageVersion, catalogVersion, catalogUpdatedAt, sourceDirty,
sourceRevision, and sealedAt. Sealing is the last write: anything that
touches the tree afterwards is drift, which is why the build prunes Python
bytecode and generator files before sealing
(hook-releases).
#Who enforces it
Three gates, all inside tama hooks install-release:
| Moment | Check | Refusal |
|---|---|---|
| Before any write | Bundled tree digests to its recorded releaseId | Bundled Tama hook release failed its integrity check |
| After content-addressed copy | releases/<id> digests to <id>; a stale pre-existing copy is deleted and recopied first | Installed Tama hook release failed its integrity check |
| Before the manifest is trusted | release.json schema equals ai.wisent.tama.hook-release.v1 | Unsupported Tama hook release manifest |
Both integrity refusals surface verbatim wherever the installer runs: the desktop's runtime install, the emergency disable's session-controller reinstall, and the emergency re-enable (runbook).
#Who reports it
The refusals are deliberately terse — they name no file. Attribution is the
job of the read-only tama hooks integrity: --release prints recorded vs
actual digest and the files written after the seal, --home digests every
installed tree against its directory name
(scripts).
#Where it lives
| Location | Role |
|---|---|
<release>/release.json → releaseId | The sealed claim |
Tama.app/Contents/Resources/tama-build.json → hookRelease | The same object embedded in build provenance, so the app displays its bundled identity without reopening the release |
hooks-runtime/releases/<release-id>/ | Content-addressed installed copy: the directory name is the claim |
hooks-runtime/installed.json → releaseId, catalogChecksum | What the last successful install recorded |
Session records → runtime.loadedReleaseId, runtime.catalogChecksum | What each live session actually loaded |
catalogChecksum is a second, narrower integrity value: the SHA-256 of the
rewritten registry JSON with the checksum field removed, recomputed by the
installer after path rewriting — it travels into every session record so the
desktop compares catalogs by checksum rather than by trust
(concepts/registry).
#Not to be confused with
- Code signing. The macOS signature authenticates the app bundle and its
binaries; the seal authenticates the hook tree independently of Apple's
formats, including after it is copied out of the bundle into
hooks-runtime/. A release passes both or it does not install. - The artifact digest.
Tama-<version>-macOS-<arch>.zip.digestis the SHA-256 of the distribution zip (releases); the seal is the identity of the hook tree inside it.tama hooks digestcomputes the former with the same tool, but they answer different questions. - The product version. Versions are claims in mutable metadata; the seal
is content. Two trees with the same
packageVersionand one changed bit have differentreleaseIds (releases).