- 18 agent specs across Software, Support, Marketing, Cross-cutting (orchestrator + 7 software + 5 support + 3 cross-cutting + leads-finder) - 8 reusable skills (handoff, pr-review, dokploy-deploy, campaign-structure, content-calendar, ticket-triage, license-lifecycle, invoice-draft) - 3 JSON schemas: handoff, deliverable, approval - Policies: autonomy levels, tool risk classification, compliance (DSGVO/GoBD/UWG) - routing.toml: model tiers T1/T2/T3 + provider config (PII gating) - Rust workspace (5 crates, all compile + 16 tests pass): - aos-mcp: tool registry + risk tagging - aos-policy: autonomy levels, budget enforcement, approval requests - aos-license: ed25519 license issue/verify (real crypto + CLI binary) - aos-ledger: integer-cents money math, gapless numbering, VAT, audit chain - aos-sync: frontmatter validator (18/18 specs pass), CLI binary - CLAUDE.md entry point with architecture overview + roadmap
94 lines
4.8 KiB
Markdown
94 lines
4.8 KiB
Markdown
---
|
|
name: license-lifecycle
|
|
version: 1.0.0
|
|
applies_to: [license-lead, license-ops-agent]
|
|
risk: high
|
|
---
|
|
|
|
# License Lifecycle
|
|
|
|
## When to use
|
|
When a task requires issuing, renewing, extending, or revoking a software
|
|
license key for a client. Issuing is high-risk because keys are
|
|
cryptographically signed and bound to client identity; revoking is always
|
|
human-approved.
|
|
|
|
## Steps
|
|
1. Read the task: client record, product/SKU, license type
|
|
(subscription / perpetual / trial), binding (domain / instance id /
|
|
seat count), and requested expiry.
|
|
2. Confirm the client's entitlement in AOS: signed contract / order covering
|
|
this SKU and quantity. No entitlement → `blocked`.
|
|
3. **Issue via the `aos-license` Rust crate only.** The crate generates an
|
|
Ed25519-signed key bound to the requested domain/instance and records
|
|
(key id, client id, product, binding, issued_at, expires_at, signing
|
|
key id). **Never invent or format a key string in the prompt** — any key
|
|
produced outside the crate is invalid and a security incident.
|
|
4. Bind the key to the domain / instance / seat count as recorded by the
|
|
crate. Rebinding requires a revoke + reissue, not an in-place edit.
|
|
5. Set expiry per the contract. For subscriptions, schedule renewal
|
|
reminders (30 / 7 / 1 days before expiry) via AOS scheduled tasks. For
|
|
trials, set the trial expiry and a single reminder.
|
|
6. Write the license record to AOS via the crate's `commit` call (the crate
|
|
owns the write). Record the **key id** (not the key itself) in the AOS
|
|
note — the key is delivered through a documented secure channel, never
|
|
pasted in a ticket or commit.
|
|
7. Deliver the key to the client via a handoff to `accounting-lead` or
|
|
`support-lead` (a welcome email → `send_client_communication` approval).
|
|
The agent never calls a send tool; first client delivery is always
|
|
human-approved.
|
|
8. For renewal / extension: re-run step 3 with the existing license id,
|
|
updated expiry, and a fresh signature. Old expiry is retained in the
|
|
audit log (GoBD — no deletion of prior state).
|
|
9. For revocation: do **not** revoke directly. Compose an `ApprovalRequest`
|
|
(`schemas/approval.json`): `action: revoke_license`, `risk: high`,
|
|
`rationale` (cancellation / abuse / chargeback / security incident),
|
|
`diff_summary` (license id, client id, product, binding, reason),
|
|
`payload: { license_id, client_id, reason, effective_at }`.
|
|
10. On approval, the policy crate calls `aos:revoke_license`. The crate marks
|
|
the license `revoked` with timestamp + reason and retains the record
|
|
(GoBD — revoked licenses are retained, never deleted).
|
|
11. Write a note on the task: action taken, license id, binding, expiry,
|
|
approver (for revocation), AOS record ref.
|
|
|
|
## Inputs
|
|
- A task with `task_id`, client id, product/SKU, license type, binding,
|
|
and expiry.
|
|
- The client's entitlement record in AOS (contract / order).
|
|
- For revocation: a documented reason (cancellation / abuse / chargeback /
|
|
security incident).
|
|
|
|
## Outputs
|
|
An `AgentDeliverable` (`schemas/deliverable.json`) with:
|
|
- `status`: `done` (issued / renewed) | `needs_approval` (revoke pending) |
|
|
`blocked` (no entitlement / missing binding / invalid request)
|
|
- `deliverables[]`: the AOS license record ref (`aos_record`), the key id
|
|
(not the key), the scheduled reminder task refs.
|
|
- `risks[]`: trial-to-paid assumptions, binding ambiguity, expiry-vs-contract
|
|
mismatch, entitlement inferred.
|
|
- `cost`: tokens only (no external spend; the crate does the signing).
|
|
|
|
## Guard rails / compliance
|
|
- **Keys are generated only by the `aos-license` Rust crate.** Any key
|
|
string produced by an LLM in the prompt is invalid and treated as a
|
|
security incident — do not paste, format, or "example" a key.
|
|
- **Revocation is always human-approved** via an `ApprovalRequest` of
|
|
action `revoke_license`. No auto-revoke, no auto-expire-shortening.
|
|
- **GoBD**: revoked and expired licenses are **retained**, never deleted;
|
|
history (issuance, renewal, revocation) is append-only in the audit log.
|
|
- **DSGVO**: license keys are credentials. Do not paste them into tickets,
|
|
commits, chat, or LLM prompts. Deliver only through documented secure
|
|
channels; first client delivery is human-approved.
|
|
- **Binding integrity**: changing the binding (domain/instance/seat) is a
|
|
revoke + reissue, never an in-place edit. Expiry must match the contract
|
|
— shorter is a client-rights violation → `blocked`.
|
|
- Reissuing a lost key requires verifying the requester is the contract
|
|
holder — never reissue on an unverified support request.
|
|
|
|
## References
|
|
- `schemas/approval.json` — `revoke_license` action
|
|
- `schemas/deliverable.json` — return envelope
|
|
- `policies/compliance.md` — GoBD retention, DSGVO, secrets
|
|
- `policies/tool-risk.md` — `aos:revoke_license` is `risk: high`
|
|
- `crates/aos-license` — Ed25519 signing, gapless license id, audit log
|