Skip to content

Latest commit

 

History

History
280 lines (234 loc) · 12.7 KB

File metadata and controls

280 lines (234 loc) · 12.7 KB

Typed verbs and coverage

Verbs are Guard's typed operation interface. A verb fixes a binary, validates parameters, describes credential and execution plans, declares consequence, and optionally supplies rollback. The daemon loads an operator catalog through --verbs or GUARD_VERBS. Foreground servers hot-reload catalog changes. The packaged Windows service loads its administrator-owned catalog once at startup.

guard verb list
guard verb show restart-service
guard verb run restart-service --param unit=nginx

examples/verbs.yaml contains command-template and coverage-cell examples. A catalog may declare platform: unix or platform: windows; Guard rejects a catalog for a different platform during linting and startup.

On file-backed deployments, operators add one catalog entry from a YAML file containing exactly one verb definition as a top-level YAML mapping:

guard verb add --file inspect-service.yaml

For both verb add --file and verb amend --file, start the file with name:. Omit the leading - list marker and the catalog's verbs: wrapper. For example, a single-verb file for inspecting a systemd service contains:

name: inspect-service
binary: systemctl
args: [status, "{unit}", --no-pager]
params:
  unit: { pattern: "^[a-zA-Z0-9@._-]+$", required: true }
consequence: reversible

The daemon validates the candidate and the complete catalog before atomically appending it. The command fails without changing the catalog when the name already exists or the definition is invalid. Generated and reserved verb identities are not accepted through this operator-authored boundary. Adding a verb requires operator authentication.

Operators replace one catalog entry from a YAML file containing exactly one verb definition:

guard verb amend restart-service --file restart-service.yaml

The client reads the live definition first and binds the amendment to its definition digest. The daemon rejects the write if another catalog edit lands between that read and the replacement. It validates the candidate and complete catalog before atomically replacing the catalog file. The replacement must retain the requested name. Runtime-generated, automatically promoted, and reserved-namespace verbs cannot be amended through this command. Like other catalog mutations, amend requires the admin bearer.

The packaged Windows service treats the installed catalog as immutable process input and disables automatic promotion. Administrators update that catalog while the service is stopped, then restart the service to load the new bytes.

Linting a catalog

guard verb lint validates a catalog file directly, without contacting or starting a daemon. It reports every invalid verb, naming the verb and the failing parameter, instead of stopping at the first failure, and exits 1 when findings exist. Linting a catalog with the new binary before swapping binaries turns a would-be startup abort into a pre-upgrade report:

guard verb lint --file /var/lib/guard/verbs.yaml
guard verb lint --fix

Without --file, lint reads GUARD_VERBS or the daemon's default catalog path. A structurally valid catalog whose verbs are not in canonical form also exits 1 and names each verb needing repair; --fix applies the same canonicalization the daemon performs at load time (operator-boundary normalization and generated-authority envelopes) and rewrites the file through the same atomic replacement path, printing each repaired verb.

Command templates

A template renders each {param} as one argv element without a shell or word splitting. Parameter patterns are fully anchored. A value cannot begin with - unless the parameter explicitly permits it, which prevents parameter and flag injection.

verbs:
  - name: service-status
    description: Show one service status
    binary: systemctl
    args: [status, "{unit}", --no-pager]
    params:
      unit: { pattern: "^[a-zA-Z0-9@._-]+$", required: true }
    consequence: reversible
    trusted: true

trusted: true skips the evaluator for a matching operation, but it does not skip the consequence gate or hard invariants. Untrusted verbs keep the evaluator as a backstop.

Parameters use token semantics by default and cannot contain whitespace. Use value_type: single_argv with a required max_length only for a narrow, bounded value that must retain ordinary spaces inside one argv element, such as an exact query or selector. single_argv values remain unsplit and reject control characters and shell operators. Automatically promoted verbs use this form only when their finite observed values contain whitespace, with the bound derived from those values.

hold: true routes every matching operation to operator approval after policy admission, including operations declared reversible. Use it for reads whose scope or sensitivity requires review, such as bulk account enumeration. The field defaults to false when omitted.

Coverage cells

Coverage cells describe regions of ordinary tool argv. They can constrain exact required and forbidden tokens, option spellings and values, positional targets, inventory, namespace, bounded fanout, an exact canonical working directory, and caller-requested environment bindings. Their actions are preauthorized, evaluate, or deny; preauthorization requires a trusted verb.

  - name: ansible-baseline
    binary: ansible
    consequence: reversible
    credential_plan: ansible-managed-ssh
    trusted: true
    coverage:
      - name: bounded-check
        action: preauthorized
        required_args: [--check]
        inventory:
          options: [-i, --inventory]
          values: [/srv/guard/inventory/production]
        fanout:
          options: [--limit]
          max: 2
        environment:
          - name: ANSIBLE_CONFIG
            source: plain
            values: [ansible.cfg]
      - name: bounded-apply
        action: evaluate
        forbidden_args: [--check]
        fanout:
          options: [--limit]
          max: 1
        override_marker: operator:ansible-apply

A non-matching cell has no decision. The check cell above allows its bounded region and does not deny apply mode, SSH inspection, or any other command. Those areas follow their own matching cells or evaluator path.

Recognized local-file operands in command and rollback templates must be absolute. The bounded grammar covers documented option values and attached short forms, including key=path and label@path payloads. It preserves Ansible's non-file forms: inventories may be comma-terminated inline host lists, extra variables may be inline values, and vault IDs may use prompt. Referenced variable files, vault clients, module-path entries, credentials, and configuration files must be absolute under the daemon host's path semantics. Kubernetes file and kustomization sources are local absolute paths or standard input, not caller-selected URLs. Executable selectors such as Helm post-renderers must be fixed absolute paths. Transport passthroughs are fixed literals in exact templates, never caller-selected generic coverage. Ambiguous command grammars that cannot be modeled safely do not receive file-path coverage. If an explicit-inventory Ansible process reports that no inventory was parsed, or that every supplied source was unusable, Guard converts exit 0 to a failure and emits a diagnostic.

Environment sources are plain, secret, and secret-file. A constraint may name exact values or a fully anchored pattern. A cell with no environment constraints cannot preauthorize a request that adds caller-controlled bindings; that request returns to the evaluator. Automatically promoted cells never preauthorize environment bindings.

cwd binds a cell to one existing, absolute canonical directory. Guard canonicalizes the caller directory before coverage resolution and revalidates it immediately before execution, so a changed directory or symlink retarget cannot reuse the cell. This bounds tools that discover configuration, plugins, or input files from a project tree. Cwd-dependent opaque carriers do not enter automatic verb promotion; an operator-authored typed verb supplies their durable authority.

Reverse matching

Raw commands and access intents reverse-match the verb catalog. Guard collects every applicable cell, so the typed catalog remains authoritative without forcing agents to translate familiar commands.

Resolution follows these constraints:

  1. Hard invariants and explicit sticky operator boundaries are absolute.
  2. Session coverage applies over baseline coverage only inside activated regions.
  3. More specific cells win over broader cells in the same scope.
  4. Compatible matches compose with the most conservative consequence.
  5. Equally specific incompatible credential, execution, or rollback plans return to the evaluator as one conflict packet.
  6. If evaluation cannot produce one safe plan, the request holds. Authorization ambiguity fails closed with an escalation handle.

Catalog name order never chooses credentials or rollback. Global generated coverage cannot defeat an explicit operator deny. A live session can evaluate past matching global generated coverage under its own intent, while protocol hard-denies and operator policy remain floors.

Successful human output stays quiet. Machine-readable run results include all applicable cells. Held or denied human output identifies matching verbs and one durable access request. Denials offer ordinary, one-time, and bounded approval; holds offer only one-time approval.

Baseline and session activation

baseline: false keeps a verb inactive until an issued grant names it. A session may activate that verb or replace matching baseline preauthorization. A baseline evaluate or deny cell with an override_marker changes only when the session carries the same operator marker. Automatically generated verbs cannot declare markers.

This split permits a readonly daemon baseline and a short-lived grant for apply mode on one host without making broad apply authority global.

Generation and promotion

guard verb create --preview safety-checks and validates a synthesized candidate, then keeps it only in a bounded in-memory review cache. The preview does not enter the active catalog. Direct creation and guard verb create --from-preview enumerate every finite parameter binding and run each rendered command through the production evaluator admission path with execution disabled. A denial, non-finite pattern, or candidate set above the admission bound prevents catalog persistence.

guard access request synthesizes typed coverage when no existing verb matches the normalized intent. Proposed verbs cannot be baseline or trusted, use a shell or interpreter binary, or accept unbounded whitespace or shell-control patterns. A bounded single_argv parameter may carry an exact finite value with ordinary spaces inside one argv element. Approval promotes only the reviewed matcher to trusted session-scoped coverage. The durable request stores the proposal and restores it from SQLite while its access session is active. Guard derives consequence locally, so unknown or mutating generated shapes remain irreversible holds. The operator-authored catalog is unchanged. Equivalent typed shapes are reused instead of duplicated.

With consequence gating active, repeated eligible evaluator approvals can promote exact observed, statically read-only shapes into trusted verbs. Parameter patterns contain only escaped values supported by evidence. Irreversible and recoverable shapes are not auto-promoted: mutating commands remain under consequence gating or operator review, and a model-proposed rollback never creates unattended authority. An auto-promoted verb never carries a consequence above reversible. Promotion records the evaluator regime, and a model or prompt change sends stale coverage back to evaluation.

Auto-promoted verbs are marked auto_promoted in guard verb list, and their coverage provenance states how it was produced: observation_replays record the observed evaluator decisions a matcher was derived from, plus the generator's own boundary example. Provenance probes are reserved for checks a generator actually executed against the finished matcher; automatic promotion records none.

API traffic uses the same verb vocabulary. Generated API cells bind endpoint, session fingerprint, full session revision, operation, namespace, body shape, protocol authority selectors, evaluator regime, and expiry. Authority selector identity includes attached option aliases, so changing an attached alias or the session revision requires a fresh evaluation. Value-bearing mutations remain evaluator-routed. Inspect or reset generated cells with:

guard verb coverage list
guard verb coverage clear

Generated coverage is an acceleration layer inside existing authority, not a new authority source.