migrations/0006_control_plane.sql: control_plane_audit_log (append-only, no UPDATE/DELETE for trf_app) and control_plane_proposed_entries (the Contributor tier's "propose, don't append" workflow from concept §2) - review decisions go through a review_proposed_entry() SECURITY DEFINER function, same governance-action pattern as set_extension_status/revoke_credential, not a direct UPDATE. src/target_revenue/control_plane.py is the enforcement layer concept §2 called for: register_phase/append_development_credit require Operator+; propose_ledger_entry requires Contributor+ and stores a pending proposal without touching the real Ledger; approve_proposed_entry (Operator+) appends it under the *reviewer's own* credential/attribution (not the original proposer's - the reviewer is who's authorizing it into the real Ledger, while the proposer stays on record in the proposal row and audit log); reject_proposed_entry (Operator+) discards it. issue_/ revoke_user_credential (Admin+) wrap registry.py's T02 functions with the same rights check and audit logging. Every action funnels through record_audit_event, independent of the Trust Service's own signed records. tests/test_control_plane.py (12 tests): rights enforcement at each tier boundary, the full propose -> approve -> appended-under-reviewer flow, propose -> reject -> nothing appended, double-review rejection, audit log content/attribution, DB-level UPDATE rejection on both new tables. Full suite: 84 offline (unchanged), 53 with Docker (up from 41); no stray containers left running.
147 lines
7.9 KiB
Markdown
147 lines
7.9 KiB
Markdown
# Target Revenue Control Plane — Concept
|
|
|
|
Status: Concept v0.1
|
|
Date: 2026-07-30
|
|
Workplan: `workplans/TREV-WP-0009-target-revenue-control-plane.md`
|
|
Primary artifacts: `specs/TrustServiceProductRequirementsDocument.md`, `src/target_revenue/service/app.py`, `specs/TRSL-Governance.md` §1 (Licensor identity), `scripts/trf_onboard.py`
|
|
|
|
**Scope note:** this is a concept, not an implementation. It defines what
|
|
the Control Plane UI is for, who uses it, and what it must and must not
|
|
be able to do — building directly on the hosted Trust Service
|
|
(`workplans/TREV-WP-0006-trust-service-implementation.md`, finished) and
|
|
the onboarding mechanism (`specs/TrustServiceOnboarding.md`) rather than
|
|
replacing either.
|
|
|
|
---
|
|
|
|
## 1. Purpose
|
|
|
|
Everything in `scripts/trf_onboard.py` today is a CLI, operated by
|
|
whoever has shell access and the Licensor's token in an environment
|
|
variable. That is adequate for the dry-run routine already exercised
|
|
(`info-tech-canon`), but not adequate for **an actual "binky tenant" user**
|
|
— someone who should be able to interactively register Phases, review
|
|
Phase status, and — the maintainer's specific requirement — **interactively
|
|
create Development Credit ledger entries**, without needing to construct
|
|
raw JSON payloads or hold the Licensor's raw API token directly in their
|
|
own shell environment.
|
|
|
|
The Control Plane is a web (or equivalent interactive) front end over the
|
|
existing hosted Trust Service API (`service/app.py`), for the `binky`
|
|
tenant specifically, not a replacement for that API or a new Trust
|
|
Service instance.
|
|
|
|
## 2. Users and rights model — the actual new design gap
|
|
|
|
**This is the concept's main open design question**, not something WP-0006
|
|
already solved: WP-0006's auth model is **one token per Licensor**
|
|
(`migrations/0001_registries.sql`'s `licensors` table), sufficient for a
|
|
script or CI pipeline acting *as* the Licensor, but not sufficient for
|
|
**multiple human users**, potentially with **different rights**, acting
|
|
*on behalf of* the same Licensor tenant (`binky`).
|
|
|
|
**Rights tiers — adopted 2026-07-30** (`workplans/TREV-WP-0009-target-revenue-control-plane.md`
|
|
T01, maintainer-accepted, as proposed):
|
|
|
|
| Right | Can do |
|
|
|---|---|
|
|
| Viewer | Read Phase status, metrics, ledger, attestations, breach records (all already public per FR-9/FR-10 — a Viewer arguably needs no new auth at all, since these are unauthenticated reads today) |
|
|
| Contributor | Everything Viewer can, plus: submit a *proposed* Development Credit entry for review (not directly appended) |
|
|
| Operator | Everything Contributor can, plus: directly append Development Credit/Remission Credit entries, register new Phases, publish breach/compliance records |
|
|
| Admin | Everything Operator can, plus: manage which human users hold which right within the `binky` tenant |
|
|
|
|
**Design tension — resolved 2026-07-30, option (a) adopted, not (b):**
|
|
WP-0006's Ledger append API (`ledger.append_entry`) currently
|
|
authenticates a single Licensor token, not an individual human user — so
|
|
"Operator Alice" and "Operator Bob" both acting for `binky` are currently
|
|
indistinguishable at the API layer (both just look like "the `binky`
|
|
token"). The maintainer selected **(a): the Control Plane issues
|
|
per-human-user sub-credentials that map to the same underlying Licensor
|
|
identity**, over this document's own recommendation of (b) — meaning a
|
|
real, scoped auth-schema extension to WP-0006's already-finished
|
|
`licensors`/token model is now a genuine prerequisite for T02/T03, not a
|
|
hypothetical branch. The payoff: the Trust Service's own signed ledger
|
|
records can attest to the specific human who acted, "Alice, acting for
|
|
`binky`, did this," not merely "the `binky` Licensor did this" — a
|
|
stronger audit guarantee than (b) would have given, at the cost of
|
|
touching WP-0006's tested auth layer. See
|
|
`workplans/TREV-WP-0009-target-revenue-control-plane.md` T02 for the
|
|
resulting scope. (This document had recommended the simpler alternative
|
|
— the Control Plane holding one server-side token and layering its own
|
|
human-user audit log in front of it, with no WP-0006 changes required —
|
|
but the maintainer's choice of (a) is the adopted answer.)
|
|
|
|
## 3. Core interactive flow: creating a Development Credit entry
|
|
|
|
The maintainer's specific requirement. Concretely, for an Operator-tier
|
|
user:
|
|
|
|
1. Select a registered Phase (from a list, via `GET /phases/{id}` reads —
|
|
already public, already implemented).
|
|
2. Fill a form for a new `development-credit` ledger entry: amount,
|
|
currency, recognized date, evidence reference, applicable Monetization
|
|
Extension (selected from `specs/CanonicalMonetizationProfiles.md`'s
|
|
catalog, not free text).
|
|
3. The Control Plane server-side calls `POST /phases/{id}/ledger`
|
|
(`ledger.append_entry`, already implemented, WP-0006-T04) using the
|
|
`binky` Licensor token it holds — the human user never sees or handles
|
|
that token directly.
|
|
4. The Control Plane records, in its own audit log (§5), which human user
|
|
initiated this specific entry, alongside the Trust Service's own
|
|
returned signed record.
|
|
5. Display the updated Phase status (`GET /phases/{id}/metrics`, already
|
|
implemented) immediately after, so the user sees the effect of what
|
|
they just did.
|
|
|
|
**Nothing here requires changing the Ledger append API itself** — the
|
|
Control Plane is a client of it, exactly like `scripts/trf_onboard.py` is
|
|
today, just interactive and multi-user rather than single-shot CLI.
|
|
|
|
## 4. Other interactive capabilities (lower priority than §3)
|
|
|
|
- Registering a new Phase, using the Development Effort Calculator
|
|
(`specs/DevelopmentEffortCalculatorConcept.md`) to help populate
|
|
`target_basis`, rather than requiring the user to hand-author a JSON
|
|
manifest.
|
|
- Viewing the Breach/Compliance Record history for a Phase and initiating
|
|
a new breach/cure/determined event (Operator+ only, per License V1C1
|
|
§7.4's Licensor-determination principle — the Control Plane must not
|
|
let this become the Control Plane's own discretionary judgment about
|
|
whether a breach occurred, consistent with TSD §4.1's forbidden-action
|
|
column).
|
|
- Viewing the Conversion Attestation once a Phase converts (read-only,
|
|
already public).
|
|
|
|
## 5. Audit logging — a Control Plane responsibility, not a Trust Service one
|
|
|
|
Every write action taken through the Control Plane must be logged with:
|
|
which human user, what action, when, and the resulting Trust-Service-
|
|
signed record's id — **in the Control Plane's own storage**, since the
|
|
Trust Service itself has no concept of individual human users under one
|
|
Licensor token (§2). This log is the answer to "who actually clicked the
|
|
button," which matters operationally even though it's not part of the
|
|
Trust Service's own cryptographic guarantees.
|
|
|
|
**Implemented 2026-07-30** (`workplans/TREV-WP-0009-target-revenue-control-plane.md`
|
|
T03, `src/target_revenue/control_plane.py`): `control_plane_audit_log`
|
|
(append-only) records every action; the Contributor tier's "propose, not
|
|
append" workflow (§2's rights table) is backed by
|
|
`control_plane_proposed_entries`, with Operator/Admin review going
|
|
through a governance-gated database function rather than a plain
|
|
UPDATE — the same pattern already established for extension
|
|
canonicalization and credential revocation.
|
|
|
|
## 6. Explicit non-goals
|
|
|
|
- Replacing `scripts/trf_onboard.py` — the CLI remains valid for
|
|
scripted/CI use; the Control Plane is for interactive human use.
|
|
- Changing the Trust Service's core guarantees (determinism, append-only,
|
|
no discretionary conversion authority) — the Control Plane is a client,
|
|
bound by the same rules any other client is.
|
|
- Deciding the human-user rights model unilaterally — §2 is a proposal
|
|
for workplan T01 to confirm, not a decided design.
|
|
- Multi-tenant support for Licensors other than `binky` — in scope
|
|
structurally (the Trust Service already supports multiple Licensors,
|
|
TS-FR-8), but this concept is scoped to the one tenant that currently
|
|
exists; extending the UI to other tenants is natural future work, not
|
|
blocking this concept.
|