--- 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