| name | least-privilege |
| description | Verify the receipt ids behind a normalized authority-usage summary, compare that evidence with granted scopes, and propose the narrowest grant the evidence supports. |
| runx | {"category":"security"} |
Least Privilege Auditor
Turn granted authority plus attributable observed usage into a bounded
attenuation proposal.
This skill resolves every supporting receipt id through the native ledger
runner before it compares a normalized usage summary with the current grant.
Native history proves that the cited receipts exist and records their status and
verification posture; the caller remains responsible for the normalized scope
observations because the history projection does not expose hydrated receipt
bodies. Missing receipt proof defers every scope. The output is a reviewable
attenuation proposal, not an automatic change.
What this skill does
- Diff granted authority against receipt-backed usage.
- Classify each granted scope as
keep, narrow, remove, or defer.
- Propose the narrowest grant that still covers observed usage.
- State residual risk after attenuation.
- Emit a receipt-quality report a reviewer can apply or reject.
When to use this skill
- Periodic least-privilege review of a skill, grant, or principal before
publish, renewal, or maturity promotion.
- After an incident, to identify authority that can be safely removed without
breaking observed behavior.
- Before expanding distribution of a public skill, to prove its grant is
minimal against real receipts.
- When a reviewer asks for a scope-by-scope evidence trail, not just a summary.
When not to use this skill
- To grant new authority. This skill only narrows; widening is a human
decision.
- When no usable receipt evidence exists. Return
needs_more_evidence rather
than guessing a grant down to nothing.
- For secret material handling or credential exposure. Use the appropriate
secret-leak triage flow instead of scope review.
- When the user asks for automatic permission changes. Produce a proposal and
stop unless a separate approved delivery lane exists.
- When grant semantics are unknown and cannot be normalized. Return
needs_input with the exact syntax or policy question.
Procedure
-
Scope the audit target.
- Identify
subject, grant source, receipt ids or receipt window, and
whether receipts are from the same principal or skill version.
- Gate: if the subject, grant list, or usage source is ambiguous, stop with
needs_input.
- Evidence expected: subject id or label, granted scope list, receipt ids or
an explicit statement that no receipts were available.
-
Normalize granted scopes.
- Parse each scope into verb, resource, path or namespace, conditions, and
wildcard breadth.
- Preserve original scope strings. Do not rewrite policy syntax casually.
- Gate: if a scope cannot be parsed, keep it as
defer and request the
missing policy semantics instead of treating it as unused.
-
Build the usage model from attributable evidence.
- Resolve each supporting receipt id through
ledger read.
- Read exercised verbs and resources from the supplied normalized usage
summary and preserve its receipt references.
- Count successful use separately from denied or dry-run checks.
- Do not infer scope usage from a successful high-level task alone; cite the
receipt step or policy check that exercised the authority.
-
Classify every granted scope.
keep: at least one observed successful use requires the granted scope as
written, or a reserved/break-glass policy explicitly requires it.
narrow: all observed uses fit a strictly smaller verb, resource,
namespace, condition, or path.
remove: no observed use, denied check, or documented reserved purpose
supports the scope.
defer: evidence is conflicting, receipt attribution is weak, or policy
semantics are unknown.
-
Propose attenuation.
- Remove scopes classified as
remove.
- Downgrade scopes classified as
narrow only when every observed use fits
the narrower grant.
- Leave
keep and defer scopes unchanged in the proposed grant.
- Gate: never produce a proposal narrower than the evidence supports. A
scope used once is used.
-
State residual risk and reviewer action.
- Name what the proposed grant can still do.
- Name any broad scope kept despite thin evidence and why.
- Separate
applyable now from needs human policy decision.
-
Emit receipt expectations.
- A valid receipt for this skill should record input grant count, receipt
sources, classification counts, proposed removals or narrowings, stop
status, and unresolved questions.
Edge cases and stop conditions
- Empty or unattributable usage evidence: return
needs_more_evidence; do not
remove all scopes by default.
- Missing granted scopes: return
needs_input; there is no baseline to diff.
- Receipt subject mismatch: return
needs_input with the mismatched subject or
version.
- Conflicting receipts: classify affected scopes as
defer and return
needs_human if the conflict changes the proposal.
- Wildcard grants: narrow only to observed resource prefixes when receipt
coverage is representative; otherwise keep and flag residual risk.
- Reserved, compliance, or break-glass scopes: keep unless the operator
provides explicit policy authority to remove them.
- Dry-run-only use: do not count as successful exercised authority unless the
grant exists solely for validation.
- Grant already matches usage: return
no_change with the evidence summary.
- User asks to hide or omit unused authority: refuse that part and report the
complete scope diff.
Output schema
Return a structured report with these fields:
status: attenuation_proposed | no_change | needs_more_evidence | needs_input | needs_human | refused
subject: string
evidence:
receipt_ids: [string]
receipt_window: string | null
grant_source: string | null
limitations: [string]
scope_diff:
- granted_scope: string
normalized:
verb: string | null
resource: string | null
conditions: object | null
observed_use:
count: number
verbs: [string]
resources: [string]
receipt_refs: [string]
classification: keep | narrow | remove | defer
proposal: string | null
rationale: string
attenuated_grant: [string]
removed_scopes: [string]
narrowed_scopes:
- from: string
to: string
kept_scopes: [string]
deferred_scopes: [string]
residual_risk: [string]
reviewer_action: applyable_now | needs_policy_decision | gather_more_receipts | none
receipt_expectations:
classification_counts: object
stop_status: string
unresolved_questions: [string]
Worked example
Input:
subject: skills/report-exporter
granted_scopes:
- drive.files.read:/reports/*
- drive.files.write:/reports/*
- drive.files.delete:/reports/*
receipt_ids: [rx_101, rx_102]
usage_summary:
observed:
- scope: drive.files.read:/reports/*
count: 8
refs: [rx_101:step_3, rx_102:step_2]
- scope: drive.files.write:/reports/*
count: 2
refs: [rx_101:step_6, rx_102:step_5]
Output:
status: attenuation_proposed
subject: skills/report-exporter
removed_scopes:
- drive.files.delete:/reports/*
narrowed_scopes: []
kept_scopes:
- drive.files.read:/reports/*
- drive.files.write:/reports/*
attenuated_grant:
- drive.files.read:/reports/*
- drive.files.write:/reports/*
residual_risk:
- The skill can still read and write any file under /reports/*.
reviewer_action: applyable_now
The delete scope is removable because no cited receipt exercised delete
authority. The read and write scopes stay because each was used at least once.
Inputs
subject (optional): skill id, grant id, principal, or other label for what
is being audited.
granted_scopes (required): the current scopes granted to the subject,
preferably in canonical policy syntax.
receipt_ids (required): exact receipt ids supporting the usage summary.
usage_summary (required): normalized receipt-derived usage with an
observed array of scope, count, and receipt refs.
receipt_rows (optional): native-projection rows for deterministic replay;
live runs resolve receipt_ids from the configured receipt store.
objective (optional): operator intent that focuses the review, such as
"prepare for public publish" or "post-incident attenuation".
policy_notes (optional): reserved scopes, compliance constraints, or
human-approved exceptions that affect removal decisions.