13 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|
| QONTO-WP-0004 | workplan | Security hardening and Knative runtime path for internet exposure | infotech | qonto-assistant | finished | claude | the-custodian | 2026-07-23 | 2026-07-27 | 81a476ac-06d5-4e98-a2ed-1518b16863aa |
Security hardening and Knative runtime path for internet exposure
Implements docs/SecurityPractice.md, written ahead of deploying
qonto-assistant to the Railiance runtime fleet. This is the first fleet
service that must be reachable by clients outside the cluster
(laptop-based agent harness sessions) while holding a real company bank
credential — a risk class above every other agent-facing service shipped so
far.
The intended runtime direction is now explicit: use reef-railiance,
establish a new rail-knative, and dynamically run a future rapp-qonto
package on that rail. This repo remains the current ownership home for the
Qonto domain code and the place where the security contract is first made
concrete, but the remaining deployment-oriented work in this plan should
feed that repo-family path rather than extend a permanent direct
qonto-assistant deployment target.
Some of this workplan's scope depends on systems this repo does not own
(key-cape, flex-auth, tenant-engine, Railiance placement). Those
dependencies are tracked explicitly per task rather than assumed away; the
kings-guard-owned portion is tracked as a separate intake against
KG-WP-0002, not duplicated here.
As of Sunday, July 26, 2026, the cross-repo framework direction for that path
is tracked in
railiance-master/workplans/RMASTER-WP-0019-knative-qonto-runtime-on-reef-railiance.md.
Tasks already marked done below remain valid as ownership-repo hardening and
migration input; the remaining open tasks are now about handing the runtime
path into reef-railiance, rail-knative, and rapp-qonto.
Task: Security Genome record and audit-stream review
id: QONTO-WP-0004-T01
status: done
priority: high
state_hub_task_id: "e93b2027-dd28-464b-8ea3-f7540f91caad"
2026-07-23: specs/security-genome.yaml written per
kings-guard/specs/NetKingdomImmuneArchitecture.md §9.1's schema —
capabilities provided/consumed, expected egress (OpenBao + Qonto API only),
data classification, recovery expectations, and explicit tolerances for
today's known-temporary gaps (shared-secret bearer auth, self-asserted actor
claims). Confirmed AuditLogger's existing event shape (actor, capability,
decision, deny_reason, latency, upstream status, policy version) already
matches the Immune Observation contract closely enough that no schema
rework is anticipated once a real consumer exists.
Done when: genome record exists and is reviewed against the blueprint schema; no code changes required for this task.
Task: Deny-escalation lockout
id: QONTO-WP-0004-T02
status: done
priority: high
state_hub_task_id: "3b10dd59-b03f-4d2b-8164-aa2a1c8c06c4"
2026-07-23: src/qonto_assistant/security_watch.py::DenyEscalationTracker
locks out an actor who triggers arg_constraint/credential_exfil policy
denials QONTO_DENY_ESCALATION_THRESHOLD times (default 3) within
QONTO_DENY_ESCALATION_WINDOW_SECONDS (default 60s), for
QONTO_DENY_ESCALATION_LOCKOUT_SECONDS (default 300s). Wired into
CapabilityService._execute ahead of the policy kernel call; on by default
via QONTO_DENY_ESCALATION_ENABLED. Ordinary denies (authz_denied,
tenant_scope, unknown_capability) never count. This needed no external
dependency — it closes a real gap using only what already exists (the audit
stream and rate limiter).
Verified: tests/test_deny_escalation.py (8 new tests: tracker unit tests
- service integration for both the escalating and non-escalating paths);
full suite
pytest→ 39 passed; REST and MCP smoke scripts both pass against fixtures;compileallclean.
Done when: threshold/window/lockout are configurable, on by default, and proven not to affect ordinary policy-deny traffic.
Task: Replace shared-secret bearer token with key-cape identity
id: QONTO-WP-0004-T03
status: done
priority: high
state_hub_task_id: "f02b1cc0-4afc-44e5-b71d-545a13a3e12b"
2026-07-24: No key-cape-side coordination was actually needed — its
/jwks is a standard RS256 endpoint. KeyCapeTokenVerifier
(src/qonto_assistant/key_cape_auth.py) fetches/caches signing keys over
httpx, validates iss/aud/exp and the IAM Profile v0.3 required claims,
and derives ActorClaims from the token. Wired into
auth.actor_claims_from_headers (the single seam both REST and MCP use); a
verified bearer token now takes precedence over self-asserted X-Actor-*
headers, and can be made mandatory via QONTO_KEY_CAPE_REQUIRED once real
tokens are issued to callers. Off by default (no
QONTO_KEY_CAPE_JWKS_URL set). QONTO_ASSISTANT_MCP_TOKEN remains a
documented local-dev/legacy fallback per docs/mcp-integration.md.
Verified: 13 new tests using a real generated RSA keypair + JWKS served
over httpx.MockTransport (valid/expired/wrong-audience/wrong-issuer/
missing-claim/unknown-key/rotated-key tokens, plus the auth.py precedence
and required-vs-optional paths).
Task: Register and enforce finance.qonto.read in flex-auth
id: QONTO-WP-0004-T04
status: done
priority: high
state_hub_task_id: "fce7bf0e-674a-461f-8f2f-42d7dde3a722"
2026-07-24: Registered qonto-assistant in flex-auth
(flex-auth/examples/qonto-assistant/, modeled directly on
examples/tenant-engine/): one resource type (finance-snapshot), one
action (finance.qonto.read), a Rego policy gating on
resource.system + action + subject.type + tenant match. Tenant
capability-role/plan liveness (VEN/CUS) is deliberately not encoded in
that policy — kept as a separate tenant-engine live-lookup check, per
both repos' policy-scope notes.
LiveAuthorizationGate (src/qonto_assistant/live_authorization.py)
combines FlexAuthCheckClient.is_allowed() (live POST /v1/check) and
TenantEngineClient.active_roles() (live GET /tenants/{id}/roles/live, gated on QONTO_TENANT_ENGINE_REQUIRED_ROLES,
default VEN,CUS) and is wired into CapabilityService._execute ahead of
the internal policy kernel. Both clients fail closed by construction.
Replaces QONTO_ASSISTANT_ENFORCE_SCOPE's cached-claim check as the live
authorization control point; off by default (no QONTO_FLEX_AUTH_URL set).
Verified beyond mocked unit tests: ran a real flex-auth serve loaded with
the registered policy (flex-auth test-policy/load-registry/check all
pass) and a real tenant-engine instance seeded with a VEN grant for
tenant:friendly:binky, and exercised this repo's actual clients against
both live processes over real HTTP — allow for the correct tenant,
live_authz_denied for a mismatched one. 28 new unit tests.
Task: Define the rail-knative internet-facing runtime contract
id: QONTO-WP-0004-T05
status: done
priority: high
state_hub_task_id: "e8a87292-488d-4871-be3e-59a5b6a38694"
Per docs/SecurityPractice.md §2/§6, the internet-facing wake path must
authenticate and authorize before waking the real service, and the backend
must still scale back to zero when idle. Under the new framework direction,
this repo should no longer assume a custom in-repo facade or a permanent direct
Deployment target. The default plan is now a new rail-knative hosted by
reef-railiance, with this repo describing the app-side contract that rail
must satisfy.
This task should define, from qonto-assistant's point of view:
- the pre-wake authn/authz behavior the rail must guarantee
- the cold-start and idle-scale semantics the package path depends on
- which behavior belongs in
rail-knativeversus the futurerapp-qontopackage versus this ownership repo - which parts of
docs/SecurityPractice.mdneed rewriting now that the target runtime path is explicit
Depends on: RMASTER-WP-0019 defining the cross-repo architecture
boundary, plus the future rail-knative bootstrap work that will follow from
it.
Done when: the required runtime contract is written clearly enough that future
rail-knative and rapp-qonto implementation work can start without
reopening the security model, and no new direct-deployment assumption is left
as the default target.
2026-07-26: Added docs/knative-runtime-and-rapp-handoff.md. It defines
pre-wake controls, cold-start/idempotency/failure behavior, raw-port exposure,
identity and credential boundaries, and the evidence required before
production approval.
Task: Isolation placement request to Railiance
id: QONTO-WP-0004-T06
status: done
priority: medium
state_hub_task_id: "7cd52269-ca0a-4b09-ab51-f02ed71604f2"
2026-07-24: Made the request concrete and reviewable (this task's own stated scope — not to implement scheduling itself). Delivered:
deploy/k8s/qonto-assistant/— dedicated namespace (not shared withactivity-core),Deploymentatreplicas: 0(meant to be scaled 0↔1 by the facade, T05),ClusterIP-onlyService, default-denyNetworkPolicy. Modeled onllm-connect's real deployment, tightened perdocs/SecurityPractice.md§7. Verified:kubectl kustomizerenders all six resources cleanly.- CCR-2026-0009 (in
railiance-platform,status: proposed) — a workload-scoped Kubernetes-auth access lane into the existingtenants/binky/qonto-apicredential, since CCR-2026-0008 is human/OIDC admin access only and unusable by a running pod. Mirrors CCR-2026-0003's llm-connect pattern. Paired draftClusterSecretStorealso added. Both validated againstschemas/credential-change-request.schema.yaml. railiance/app.toml— staged-promotion contract,criticality = "critical"(mandatory human approval before Stage 2 exposure and Stage 3 promotion, per the contract's own rule for production-critical workloads). Validated againstrailiance-cluster/schemas/railiance-app.schema.json.
Still pending, explicitly not this task's to resolve: CCR-2026-0009
needs platform-operator + binky-tenant-owner approval; the actual isolation
tier (I1 vs I2) is Railiance's scheduling call once the manifests are
reviewed; whether the facade (T05) co-locates in this namespace is assumed
in networkpolicy.yaml but not yet decided.
2026-07-26 direction update: this direct Kubernetes placement package now
serves as migration input and isolation evidence rather than the intended final
runtime topology. Future implementation should treat these manifests, the CCR,
and the staged-promotion contract as baseline material to be re-homed into the
reef-railiance + rail-knative + rapp-qonto path, not as the long-term
deployment target of this repo.
Task: Plan the rapp-qonto extraction and runtime handoff
id: QONTO-WP-0004-T08
status: done
priority: high
state_hub_task_id: "e265cce1-df09-48ca-a0fd-f38897c44b4c"
Define how the current qonto-assistant deployment-oriented material hands off
into a future rapp-qonto package repo while preserving this repo as the
ownership home for the Qonto domain logic.
At minimum, this task must name:
- what stays in
qonto-assistant - what becomes package/runtime content in
rapp-qonto - how
KEY-WP-0004's workload-identity lane and the OpenBao secret lane bind to the future runtime principal - which current files are migration input only and must stop growing in place
Done when: the extraction and handoff boundary is written; the dependency on
RMASTER-WP-0019 is explicit; and no new direct
deploy/k8s/qonto-assistant implementation starts without an explicit
exception note.
2026-07-26: The handoff document now assigns domain behavior and application
policy to this repo, package/runtime assets to rapp-qonto, and generic
activation/revision behavior to rail-knative. Existing direct Kubernetes
assets are explicitly migration input.
Task: Closure review
id: QONTO-WP-0004-T07
status: done
priority: low
state_hub_task_id: "2a2da206-d182-4a72-aea4-56e58ae74c76"
Close when T03–T06 and T08 land or are explicitly deferred with a recorded
reason. T01/T02 already shipped without waiting on external dependencies, and
T06 now counts as migration input rather than the final runtime shape. Run
statehub fix-consistency.
2026-07-27: Closure review passed. T03–T06 and T08 are complete, the
ownership/runtime split is implemented by rapp-qonto and rail-knative,
the OpenBao-backed secret lane is live, and restricted Qonto egress is
enforced by a controlled proxy plus a fail-closed startup gate. Remaining
deployment, rollback, and reef risk evidence belongs to
REEF-RAILIANCE-WP-0003, not this source-repo hardening plan.