Skip to content
Development
Skill

/security-tiers

Use when classifying any operation before executing it, or deciding whether user approval is required

From plugin
metraton-gaia
339 skills9 agents11 hooks
Install
$ npx -y skills add metraton/gaia --skill security-tiers --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/security-tiers

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when classifying any operation before executing it, or deciding whether user approval is required

SKILL.md

security-tiers.SKILL.md
name: security-tiers
description: Use when classifying any operation before executing it, or deciding whether user approval is required

Security Tiers

COMMAND_SET boundary

Classification applies to each exact atomic command. After read-only investigation, one or more predictable T3 commands may be requested plan-first with `gaia approvals request-set` -- a single predictable T3 command uses the same verb proactively, not only the reactive path that begins after PreToolUse returns `[T3_BLOCKED]`. Never discover a set by attempting a compound shell command. Group only one bounded goal with exact known order and coherent risk, rollback, and verification. Those last two are supplied on the request itself -- `--rollback` and `--verification`, alongside `--rationale` and one `--command` per item -- and are sealed at mint, then rendered verbatim as the `ROLLBACK` and `VERIFICATION` fields of the surface the user consents against; requesting without them asks for consent while the surface states they are absent. Do not group speculative or output-dependent steps. Consent grouping is not execution atomicity: execution remains one command per call, ordered and fail-fast.

security-tiers classifies every operation into four tiers so an agent knows whether it can run freely or must request the user's consent.

The four tiers

| Tier | What it is | Approval? | Example verbs | |------|------------|:---:|---------------| | **T0** | Read-only; observes state, changes nothing | No | get, list, describe, show, logs, status | | **T1** | Local validation; no remote calls, no state | No | validate, lint, fmt, check | | **T2** | Simulation / dry-run; may read remote, never writes | No | plan, diff, --dry-run, template | | **T3** | State-mutating; creates, updates, or destroys | **Yes** | apply, create, delete, push, deploy |

**A fifth verdict exists OUTSIDE this ladder: the categorical denial** -- blocked commands, the `.claude`/hooks tree targeted through *Bash* (`hooks/modules/security/protected_path_guard.py::check`), the DB-write guards, and a file write into a git working tree whose author is the SHELL (`hooks/modules/security/shell_write_guard.py::check`). It is not a tier, it is not approvable, and no grant lifts it.

That last one is categorical for a reason worth stating, because it looks at first glance like it should be approvable: **it withholds no capability.** A grant is scoped to a TOOL AND A PATH, never to an effect, so consenting to a shell writer would be consenting to a channel rather than to a change -- and the change itself is already permitted, byte for byte, through `Write`/`Edit`. What the guard refuses is the channel that breaks ATTRIBUTION: a redirect, `tee`, `sed -i` or `dd of=` presents the gate a command string instead of the destination, so the boundary is evaluated against the wrong object and the write lands with no tool call naming the file. Signing a permission for a channel that exists precisely to leave no trace buys nothing. The rule needs BOTH halves to fire -- shell-authored AND resolving under a git tree -- which is why `/dev/null`, `/tmp` and `~/.gaia/scratch` still pass: they fall out of the rule, not off an allow-list. A consequence to know before it surprises you: in-tree `sed -i` is therefore NO LONGER the T3 ask-dialog that `_classify_sed` would route it to; the guard returns first, with no `approval_id`. Outside a working tree that same `sed -i` still reaches the ordinary approvable path.

The counter-argument was weighed and rejected, and is recorded because it is real and will resurface: `Write`/`Edit` force a file's contents through the agent's context and from there into the transcript, while `sed -i` transforms without anyone reading. For a file holding sensitive material the shell is better for confidentiality and worse for traceability. It was rejected because in these repositories secrets do not live in versioned files -- they live in Secret Manager, and the GitOps repo carries a check that refuses plaintext secrets at commit. A file with sensitive material sitting inside a git tree is the underlying problem, not the way it gets edited. If a concrete case ever appears, it reopens with that case in hand. **A protected path targeted through *Write*/*Edit* is a different verdict: T3-approvable**, not categorical -- a `FILE_WRITE` grant lifts it (`hooks/modules/security/approval_grants.py::check_approval_grant_for_file`); see "The `.claude` rule" below for the exact split. **The operative read for any denial: one carrying an `approval_id` has a consent path; one with none is categorical -- never report a categorical denial as T3**, since T3 names a consent request and the request they answer cannot change the outcome.

`git commit` and `git add` are **not** T3 -- they are local-only operations (they touch the working tree and local refs, never remote state), so they classify as safe by elimination. Only `git push` mutates remote state and is T3. This matches `GIT_LOCAL_SAFE_SUBCOMMANDS` in `mutative_verbs.py`, where `commit` and `add` are listed as local-safe.

**T3 gates a direction, not a category of verb.** An operation needs consent because it moves the system toward *more* capability (it grants) or *less* recoverability (it destroys). An operation that only moves the other way -- that *reduces* capability already granted -- does not need consent, because the worst it can do is take back power that was given. So within Gaia's own consent layer, `gaia approvals revoke|reject|reject-all|clean` are **not** T3: they only revoke or discard grants Gaia itself issued, never reaching outside the local approval store. The asymmetry is deliberate -- `gaia approvals approve` *grants* capability without passing through the user's consent surface at all, so it stays T3. This is anchored to the `gaia approvals` group in `CONSENT_REDUCING_SUBCOMMAND_EXCEPTIONS` (`mutative_verbs.py`), not generalized to every CLI's "revoke" -- a cloud IAM revoke is a

Read more
Ships withmetraton-gaia

Generative AI Architecture

Get the whole plugin

Other skills on metraton-gaia.