feat(license): local issuance log for minted blobs
generate_license.py now appends every minted license to ~/.datatools-creator/issued.jsonl (overridable via env). This is the creator-side system of record until the server-side flow lands. The full blob is stored alongside name/email/tier/expiry so buyers who lose their delivery email can be re-served without re-minting. File is created mode 600 and lives outside the buyer-facing ~/.datatools/ dir so it never gets bundled into a shipped install. Log failures are non-fatal (warning to stderr) — the mint already succeeded by the time we try to log, and forcing a re-mint after a log error would invalidate any device the buyer had activated. Pass --no-log for test mints. ADMIN.md adds a "Customer record-keeping" section with the path, schema, jq one-liners, and migration note pointing at the upcoming LICENSE-SERVER.md design doc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -165,6 +165,73 @@ python -m src.license_cli deactivate
|
||||
|
||||
---
|
||||
|
||||
## Customer record-keeping — the issuance log
|
||||
|
||||
Every successful `scripts/generate_license.py` run appends one JSON
|
||||
line to a local **issuance log**. This is the creator-side system of
|
||||
record for "who has a license" until the server-side flow in
|
||||
`docs/LICENSE-SERVER.md` lands.
|
||||
|
||||
**Path:** `~/.datatools-creator/issued.jsonl` (override with
|
||||
`$DATATOOLS_ISSUANCE_LOG`). Mode 600. Outside the buyer-facing
|
||||
`~/.datatools/` dir so it never gets bundled into a shipped install.
|
||||
|
||||
**Format** — one record per line:
|
||||
|
||||
```json
|
||||
{
|
||||
"license_key": "DT1-CORE-5dd8e1db-d90c4656",
|
||||
"name": "Michael Dombaugh",
|
||||
"email": "michael.dombaugh@gmail.com",
|
||||
"tier": "core",
|
||||
"issued_at": "2026-05-13T22:10:27Z",
|
||||
"expires_at": "2031-05-13T22:10:27Z",
|
||||
"blob": "DTLIC2:..."
|
||||
}
|
||||
```
|
||||
|
||||
The full blob is stored so you can re-deliver to a buyer who lost
|
||||
their email without re-minting (the re-minted blob would have a
|
||||
different signature and would invalidate any device they'd already
|
||||
activated against the old one).
|
||||
|
||||
**Useful operations:**
|
||||
|
||||
```bash
|
||||
# Full list of issued licenses
|
||||
cat ~/.datatools-creator/issued.jsonl | jq
|
||||
|
||||
# Find by buyer email
|
||||
jq -r 'select(.email == "buyer@example.com")' ~/.datatools-creator/issued.jsonl
|
||||
|
||||
# Count by tier
|
||||
jq -r .tier ~/.datatools-creator/issued.jsonl | sort | uniq -c
|
||||
|
||||
# Licenses expiring in the next 30 days
|
||||
jq -r 'select(.expires_at < "'"$(date -u -d '+30 days' +%Y-%m-%dT%H:%M:%SZ)"'") | .email' \
|
||||
~/.datatools-creator/issued.jsonl
|
||||
|
||||
# Re-deliver a buyer's blob
|
||||
jq -r 'select(.email == "buyer@example.com") | .blob' \
|
||||
~/.datatools-creator/issued.jsonl
|
||||
```
|
||||
|
||||
**Skipping the log** for test mints: pass `--no-log`. Never use this
|
||||
for real buyer fulfillment — an unlogged mint is invisible to every
|
||||
future query and to the eventual server-side migration.
|
||||
|
||||
**Backup:** treat this file like a small business ledger. Copy it
|
||||
into your password manager / encrypted cloud sync alongside the
|
||||
private key. Losing it doesn't break anything cryptographically (you
|
||||
can still mint new licenses) but it does lose the customer list.
|
||||
|
||||
**Migrating to the server:** the JSONL schema is intentionally close
|
||||
to the planned `licenses` table in `docs/LICENSE-SERVER.md`. Once the
|
||||
server is up, a one-shot import script will read the JSONL and
|
||||
insert each row.
|
||||
|
||||
---
|
||||
|
||||
## Recovery — what if the private key is lost?
|
||||
|
||||
Existing licenses keep working until they expire (the public key in the
|
||||
@@ -197,3 +264,5 @@ independent secure locations.
|
||||
| `src/license/features.py` | Tier → features mapping |
|
||||
| `src/license_cli.py` | End-user `activate` / `status` / `renew` / `deactivate` |
|
||||
| `~/.datatools/license.json` | Where activated licenses are stored on each machine |
|
||||
| `~/.datatools-creator/issued.jsonl` | Creator-side issuance log (one JSON line per mint) |
|
||||
| `docs/LICENSE-SERVER.md` | Design for the future online issuance + record-keeping system |
|
||||
|
||||
Reference in New Issue
Block a user