Skip to content

Security: alebgl77/tierdecay

SECURITY.md

Security Policy

Supported versions

Version Supported
latest tagged release yes
main / unreleased no

Only the latest tagged release is supported and recommended for production. main contains unreleased development work; use it for evaluation and contribution, not production deployment.

Reporting a vulnerability

Report privately through GitHub's private vulnerability reporting:

https://github.com/alebgl77/tierdecay/security/advisories/new

Please do not open a public issue for security matters — that discloses the problem before there is a fix. We aim to acknowledge a report within 72 hours and will coordinate a fix and disclosure timeline with you from there.

Scope & threat model

TierDecay is markdown instruction files plus a Bash install.sh that copies those files into your repository. There is no network service or runtime daemon, no telemetry, and no secret handling — so the usual server-side and credential surfaces do not apply here. The Claude Code adapter's local guard hook requires Node.js to parse tool payloads; it is the only adapter-specific runtime requirement and does not run as a service. Three surfaces are real and in scope:

1. install.sh file operations. The installer runs in your shell with your permissions and writes into your working tree (CLAUDE.md, AGENTS.md, .claude/, .tierdecay/, etc.). It refuses to run from the checkout itself and writes *.tierdecay sidecars rather than clobbering existing files, but it is still a script that touches your filesystem. Read it before running it, run it from the repo you intend to equip, and review the diff it produces. Bugs in its path handling, overwrite guards, or detection logic are in scope. Uninstall never removes learned state. If an owned artifact's recorded source is missing, uninstall preserves the installed file and relinquishes the selected target's ownership rather than treating the missing source as proof that deletion is safe. Review any preserved files yourself.

2. The instruction files themselves. This is the interesting surface. The adapters and the compiled playbook steer an autonomous coding agent, so a malicious edit to an adapter file, or a poisoned playbook entry, can influence what that agent does in your repository — the injection lives in context, not in code. TierDecay's integrity invariants (core/SPEC.md §5) exist precisely to contain playbook self-poisoning: only the orchestrator writes the ledger and playbook, executor diffs touching them are rejected at VERIFY, any acceptance failure while an entry was referenced quarantines it, and the playbook is hard-capped. A defect that lets an executor write state, lets a quarantined entry keep applying, or otherwise bypasses a §5 invariant is a security bug and in scope — as is a supply-chain edit that smuggles hostile instructions into an adapter or template.

3. Distribution and shared-repo surface. The files are trivially forkable, and the playbook is loaded into the agent's context every session — which makes two indirect attacks realistic:

  • Look-alike / tampered checkout. Anything named "tierdecay" is not this project. Install from a tagged release of this repository, not from an arbitrary clone of main or a fork: releases from this repo carry a SHA256SUMS asset — verify the archive against it before running install.sh (sha256sum -c SHA256SUMS on Linux or shasum -a 256 -c SHA256SUMS on macOS). Read install.sh and the adapter you install; they are short on purpose.
  • Shared repositories. If you commit .tierdecay/ (or the Claude Code adapter's ledger/playbook under .claude/), you share learned state with your team — and anyone with write access to the repo can steer every teammate's agent through a poisoned playbook entry. Choose deliberately: commit the state and review playbook diffs with the same scrutiny as code, or add it to .gitignore and keep state per-machine (no shared poisoning, no shared learning). Never let a playbook diff through review unread.

Enforcement layers (what is a guarantee, and what is not)

The §5 integrity invariants are protocol-level rules interpreted by a language model, plus whatever technical enforcement your CLI can add. Be precise about which layer you are relying on:

  1. Protocol (all adapters): only the orchestrator/DISTILL phase writes state; VERIFY rejects executor diffs touching it. This is an instruction a model follows — strong in practice, but the same class of mechanism as a prompt injection, so it must not be your only layer.
  2. Technical (Claude Code adapter): executor and heavy-executor carry a defense-in-depth PreToolUse guard hook (.claude/hooks/tierdecay-guard.sh). It canonicalizes structured target paths and blocks observable shell references to .claude/ or .tierdecay/; the shipped settings.json additionally ask-gates state writes behind explicit human approval. This is not a security boundary: dynamic Bash that constructs a target without a literal state-path reference, and TOCTOU changes between inspection and execution, cannot be ruled out. VERIFY therefore remains mandatory. For a strong guarantee, remove Bash from executor tools or enforce OS-level filesystem isolation. Plugin variant: Claude Code ignores hooks: frontmatter on plugin agents, so the plugin registers the same guard as a session-wide PreToolUse hook with --executors-only. It enforces only when the hook payload's agent_type names executor or heavy-executor (optionally tierdecay:-prefixed) and passes every other caller. It therefore relies on the client reporting agent_type for subagent tool calls; a client that omits it leaves plugin executors unguarded at this layer. A plugin cannot ship permission rules either: add the two ask rules yourself (tierdecay-init prints them). Run the active-hook smoke check against the plugin too. Codex adapter: .codex/hooks.json registers the same guard with --executors-only on PreToolUse for apply_patch and Bash; it parses every file header of an apply_patch envelope and resolves each target against the session directory. It shares the plugin variant's dependency on agent_type (Codex reports it inside subagents), runs only after you trust it with /hooks, and executor roles additionally run in the workspace-write sandbox. Antigravity adapter: no hook is shipped (its hook input does not yet identify the calling subagent reliably); layers 1 and 3, the executors' sandbox command policy, and tierdecay doctor carry integrity there. MCP server: tierdecay mcp is read-only by default — every tool is annotated readOnlyHint and none writes. The single write tool, tierdecay_record, exists only when the server is started with --allow-ledger-append true; MCP tools are visible to every agent in a client session, so enable it only for the orchestrator's own session. The append itself validates the row, rejects duplicates, and writes atomically under an exclusive lock.
  3. Human: review state diffs like code (see above), and treat an unexpected prompt to approve a state write as a red flag — that prompt IS the alarm going off.

A bypass of layer 2, or a shipped default that weakens it, is a security bug and in scope.

Repository regression suites exercise guard payloads and native Windows paths; the model-policy test checks aliases and hook registration/frontmatter shape. These are not live Claude Code integration tests. Platform- or capability-dependent fixture skips are reported explicitly, and security failures must not be suppressed. After an upgrade, review and merge sidecars without dropping learned state or hardened permissions, reload the session and agents, and run the active-hook smoke check in the native adapter guide using a disposable project. A passing structural check cannot prove a client's hook is active.

Out of scope: the behavior of the underlying coding CLIs and models themselves, and anything a user does after editing the shipped files by hand. The invariants assume the files you install match the ones in this repository — verify what you install.

There aren't any published security advisories