cli

package
v1.0.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 20, 2026 License: MIT Imports: 40 Imported by: 0

Documentation

Overview

F4 — Audit Logging by Permission Tier.

Every CLI invocation that flows through cobra's dispatcher must leave a single row in audit_log with the action format

cli.<tier>.<verb>

where <tier> is the lowercase PermLevel name (metaread / secretwrite / secretread) and <verb> is the cobra command path with spaces replaced by dots (e.g. "env list" -> "env.list", "audit prune" -> "audit.prune").

Examples (gate G7 — master-plan.md §5):

tene list                  -> cli.metaread.list
tene env list              -> cli.metaread.env.list
tene set FOO bar           -> cli.secretwrite.set
tene audit prune --force   -> cli.secretwrite.audit.prune
tene get FOO               -> cli.secretread.get

Design notes:

  • The row is written BEFORE RunE so that failed runs still leave an audit trail of the attempt. The deliberate trade-off: a command that errors out before doing any work still appears in the log, which is correct for security audit (we want to see attempts, not just successes).

  • Args are NEVER recorded in the action column. `tene set MY_KEY SECRET_VALUE` writes only `cli.secretwrite.set` — the key name goes in the resource column, the value is never recorded anywhere by F4. Privacy invariant (master-plan.md §8 I-5 echo).

  • The existing audit rows (vault.init, secret.read, secret.write, env.switch, env.create, env.delete, secrets.export, secrets.import, secrets.inject, secret.delete, vault.passwd_changed, vault.recovered) keep firing exactly as before. F4 is ADDITIVE: one CLI invocation -> 1 cli.* row + N existing rows. Total audit log volume roughly doubles (Q3 user decision, master-plan.md §7).

  • Failure to write the audit row must NEVER block the CLI's primary work. emitCliAuditRow swallows every error path (vault not yet created, DB locked, etc.). The user-facing behaviour of every command is independent of whether the audit write succeeded.

  • `tene init` is special-cased: the vault.db does not exist when rootPersistentPreRunE fires, so the row would silently no-op. init.go therefore calls emitCliAuditRow at the end of its RunE (after the vault is created) so G7 still holds for init.

F8 — `tene audit` subcommand surface.

Three subcommands sit under the `audit` parent:

  • `tene audit tail [-n N] [--json]` — last N rows, newest first.
  • `tene audit show [--since DUR] [--filter PAT] [--json]` — filtered query.
  • `tene audit prune --older-than DUR [--force] [--dry-run]` — delete old rows.

Tier mapping (declared once in internal/auth.CommandTier and asserted at startup by F2's auth.Validate):

  • audit PermMetaRead (catch-all root, prints help)
  • audit tail PermMetaRead (read-only metadata query)
  • audit show PermMetaRead (same — filtered query)
  • audit prune PermSecretWrite (destructive — requires master-key unlock)

`audit prune`'s PermSecretWrite tier is declared centrally but F2 deliberately leaves master-key unlock to each subcommand's RunE. runAuditPrune therefore calls loadOrPromptMasterKey before executing the DELETE so the contract is honoured at the place users feel it (the password prompt). The actual SQL deletion lives in vault.PruneAuditLog (the G10 chokepoint).

NDJSON output choice (`--json`): one JSON object per line, no surrounding array, no commas between rows. This matches the append-only stream nature of audit_log and is friendly to `jq -c`, `grep`, and incremental consumers that read line by line. Wrapping in a JSON array would force callers to load the whole result into memory before parsing.

F5 — `tene permissions` info command.

Renders the declarative permission tier map (internal/auth.CommandTier) as a human-readable table or a machine-readable JSON document. The command itself is registered as PermMetaRead in F2's tier map, so invoking it never prompts for the master password and never opens the vault for any decryption — it operates purely on the static table.

Why a dedicated command rather than `tene --help`?

  • `tene --help` is cobra's auto-generated synopsis; mutating it to embed the tier table would couple help text formatting to the auth package. Keeping permissions as its own verb lets `--json` produce a structured shape that scripts and AI agents can parse.
  • The tier table is the single source of truth for "which commands need my password?". Surfacing it as a first-class CLI verb makes that contract greppable by curious users and reviewable in CI (TestPermissions_Text_AllEntriesPresent below asserts the verb's output stays in sync with the table).

Output format (text mode):

COMMAND                TIER           PASSWORD?
-----------------------------------------------
audit                  metaread              no
audit show             metaread              no
audit tail             metaread              no
completion             metaread              no
... (sorted alphabetically WITHIN each tier)
audit prune            secretwrite          yes
delete                 secretwrite          yes
... (PermSecretWrite block)
export                 secretread           yes
get                    secretread           yes
... (PermSecretRead block)

Total: 26 commands  (16 metaread / 5 secretwrite / 5 secretread)

JSON mode (`--json`) shape:

{
  "ok": true,
  "count": 26,
  "byTier": { "metaread": 16, "secretwrite": 5, "secretread": 5 },
  "commands": [
    {"name": "audit",        "tier": "metaread",    "requiresUnlock": false},
    {"name": "audit prune",  "tier": "secretwrite", "requiresUnlock": true},
    ...
  ]
}

The JSON `commands` array is sorted by (tier-order, name) so consumers can rely on a stable byte ordering. Tier order matches the text table: metaread → secretwrite → secretread.

F8 — audit log size threshold notice.

The state machine (design.md §6B.4):

start → CheckSize:
          size < threshold → quiet (no work)
          size ≥ threshold → CheckSentinel:
                               sentinel mtime < 24h → quiet
                               sentinel missing or stale → Warn
                                  → stderr line + touch sentinel

Why one notice per 24h rather than always: master-plan §1 RISK H + prd.md §5 Failure Mode H call out that emitting the warning on every command would turn into spam and trigger "learned ignore" behaviour from the user. A 24h refresh window is the design's compromise — visible enough to drive eventual action, infrequent enough not to bury the rest of stderr.

Why a sentinel file rather than an in-process counter: a tene CLI invocation is short-lived (no daemon), so in-memory state would not survive the next run. A sentinel under ~/.tene/.audit-warned-<dir-hash> is durable across invocations and isolated per project (matches the F6 keychain-fallback notice convention so a user grokking either feature only learns one mental model).

Why per-project hashing: a user with three vaults on the same machine should get three independent notices, not have one project's warning suppress the others.

Failure modes:

  • Cannot resolve HOME → skip notice silently. The CLI's primary work must never be blocked by an inability to write a notice.

  • Cannot stat / mkdir / create sentinel → emit notice anyway and skip the touch. The next run will re-emit (slightly annoying but no information loss).

  • Size query errors → skip notice silently. We do not want a vault-read regression to bubble into the dispatcher path.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Execute

func Execute() error

Execute runs the root command.

func RootCmd

func RootCmd() *cobra.Command

RootCmd returns the root cobra command. Useful for docs/manpage generation.

func SetVersion

func SetVersion(v, c, d string)

SetVersion sets build-time version information.

Types

type App

type App struct {
	Vault    *vault.Vault
	Keychain keychain.KeyStore
	Dir      string // project directory
	Env      string // active environment
	JSON     bool   // --json flag
	Quiet    bool   // --quiet flag
}

App holds the dependencies needed for CLI execution.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL