Expose checkpoint, revoke, reinstate and sweep on the CLI
This commit is contained in:
parent
2d05f72656
commit
949feb7a32
74
sigil.py
74
sigil.py
@ -5,7 +5,12 @@
|
||||
sigil.py keygen <path> [pass] write an Ed25519 key (0600, encrypted if given)
|
||||
sigil.py enrol <agent> <pubkey.b64> <secret>
|
||||
sigil.py challenge <agent> nonce to sign
|
||||
sigil.py verify-chain check the audit log for tampering
|
||||
sigil.py checkpoint <key> <agent> [label] sign the current audit head
|
||||
sigil.py verify-chain check the chain and its checkpoints
|
||||
sigil.py revoke <agent> [--until UNIX] [--reason R] [--actor A]
|
||||
--until omitted means permanent
|
||||
sigil.py reinstate <agent> [actor] lift a revocation
|
||||
sigil.py sweep prune stale nonces and rate rows
|
||||
sigil.py sign <keypath> <nonce> [pass] signature, base64
|
||||
sigil.py verify <nonce> <sig.b64> agent session token
|
||||
sigil.py code <agent> <keypath> [pass] one-time code, proving the key
|
||||
@ -98,10 +103,71 @@ def main(argv) -> int:
|
||||
session["may_act_as_agent"] = may_act
|
||||
out(session)
|
||||
|
||||
elif cmd == "checkpoint":
|
||||
# Signs the chain head with a key the database does not hold, which
|
||||
# is what makes a later rewrite detectable.
|
||||
if len(argv) < 4:
|
||||
print("usage: sigil.py checkpoint <keypath> <agent> [label] [pass]")
|
||||
return 2
|
||||
private = keys.load_private(argv[2], argv[5] if len(argv) > 5 else None)
|
||||
out(store.checkpoint(private, argv[3],
|
||||
argv[4] if len(argv) > 4 else None))
|
||||
|
||||
elif cmd == "verify-chain":
|
||||
result = store.verify_chain()
|
||||
out(result)
|
||||
return 0 if result["ok"] else 1
|
||||
chain = store.verify_chain()
|
||||
anchors = store.verify_checkpoints()
|
||||
out({"chain": chain, "checkpoints": anchors})
|
||||
return 0 if chain["ok"] and anchors["ok"] else 1
|
||||
|
||||
elif cmd == "revoke":
|
||||
# Positional optional args are a trap here: "revoke alice please"
|
||||
# would otherwise try to parse "please" as a timestamp. Named flags
|
||||
# are unambiguous, and a number only counts as a time if it parses.
|
||||
allowed = {"until", "reason", "actor"}
|
||||
flags, rest = {}, []
|
||||
i = 2
|
||||
while i < len(argv):
|
||||
if argv[i].startswith("--"):
|
||||
key = argv[i][2:]
|
||||
if key not in allowed:
|
||||
print(f"unknown flag --{key}; expected one of "
|
||||
f"{', '.join('--' + a for a in sorted(allowed))}")
|
||||
return 2
|
||||
flags[key] = argv[i + 1] if i + 1 < len(argv) else ""
|
||||
i += 2
|
||||
else:
|
||||
rest.append(argv[i])
|
||||
i += 1
|
||||
if len(rest) != 1:
|
||||
print("usage: sigil.py revoke <agent> [--until N] [--reason R]"
|
||||
" [--actor A]")
|
||||
if rest:
|
||||
# Refusing beats guessing which agent was meant.
|
||||
print(f"expected exactly one agent name, got {rest}")
|
||||
return 2
|
||||
until = None
|
||||
if "until" in flags:
|
||||
try:
|
||||
until = int(flags["until"])
|
||||
except ValueError:
|
||||
print(f"--until must be a unix timestamp, got {flags['until']!r}")
|
||||
return 2
|
||||
killed = identity.revoke(
|
||||
store, rest[0], until=until,
|
||||
reason=flags.get("reason"),
|
||||
actor=flags.get("actor", "system"),
|
||||
)
|
||||
out({"agent": rest[0], "revoked": True,
|
||||
"permanent": until is None, "sessions_revoked": killed})
|
||||
|
||||
|
||||
elif cmd == "reinstate":
|
||||
identity.reinstate(store, argv[2],
|
||||
argv[3] if len(argv) > 3 else "system")
|
||||
out({"agent": argv[2], "revoked": False})
|
||||
|
||||
elif cmd == "sweep":
|
||||
out(store.sweep())
|
||||
|
||||
elif cmd == "events":
|
||||
out(store.events(argv[2] if len(argv) > 2 else None,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user