Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
# Security Practice — Internet Exposure, Governance, and Scale-to-Zero
|
|
|
|
|
|
|
|
|
|
|
|
> Status: draft, 2026-07-23. Written before deployment to `railiance01`.
|
|
|
|
|
|
> Context: this is the first fleet service that (a) must be reachable by
|
|
|
|
|
|
> clients outside the cluster (laptop-based Claude Code/Cursor/Codex
|
|
|
|
|
|
> sessions), and (b) is the sole holder of a real company bank credential.
|
|
|
|
|
|
> That combination raises the risk class above every other agent-facing
|
|
|
|
|
|
> service shipped so far (`llm-connect`, mail-triage), which are
|
|
|
|
|
|
> cluster-internal only.
|
|
|
|
|
|
|
|
|
|
|
|
Related: `specs/ArchitectureBlueprint.md` (service architecture and v1
|
|
|
|
|
|
policy), `docs/mcp-integration.md` (current auth model and its called-out
|
|
|
|
|
|
gaps), `docs/operator-runbook.md` (day-2 operation).
|
|
|
|
|
|
|
|
|
|
|
|
This document assumes the [`kings-guard`](../../kings-guard) NetKingdom
|
|
|
|
|
|
Immune Architecture exists as designed in
|
|
|
|
|
|
`kings-guard/specs/NetKingdomImmuneArchitecture.md` (currently draft, no
|
|
|
|
|
|
implementation) and describes how `qonto-assistant` would sit inside it,
|
|
|
|
|
|
alongside the real, already-running NetKingdom components (`key-cape`,
|
|
|
|
|
|
`flex-auth`, `tenant-engine`, OpenBao/`ops-warden`). Concrete requirements
|
|
|
|
|
|
handed to `kings-guard` to prioritize its own build are tracked as an
|
|
|
|
|
|
intake — see `KG-WP-0002` and the corresponding intake record.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Why this service is a different risk class
|
|
|
|
|
|
|
|
|
|
|
|
| Property | Prior services (`llm-connect`, mail-triage) | `qonto-assistant` |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| Client population | in-cluster jobs/timers only | in-cluster **and** external harness sessions (laptops) |
|
|
|
|
|
|
| Network exposure | cluster-internal DNS only | needs a real internet-reachable address |
|
|
|
|
|
|
| Credential held | none / low-value | live company bank API key (`tenants/binky/qonto-api`) |
|
|
|
|
|
|
| Data classification | operational metadata | tenant-confidential financial data |
|
|
|
|
|
|
| Blast radius if compromised | mail queue drift | real financial data disclosure; reputational and possibly regulatory impact |
|
|
|
|
|
|
|
|
|
|
|
|
Every control below exists to answer one question: **what stops an
|
|
|
|
|
|
internet-reachable process, holding a real bank credential, from becoming
|
|
|
|
|
|
the fleet's first genuinely damaging incident?**
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Layered design
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Internet
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ Facade / Activator (always-on, holds NO bank credential) │
|
|
|
|
|
|
│ - key-cape token verification │
|
|
|
|
|
|
│ - flex-auth pre-check (finance.qonto.read) │
|
|
|
|
|
|
│ - wakes the real service only after both pass │
|
|
|
|
|
|
│ - idle-timeout scale-back-to-zero │
|
|
|
|
|
|
└───────────────────────────┬───────────────────────────────────┘
|
|
|
|
|
|
│ (only on authenticated+authorized wake)
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ qonto-assistant (scaled 0↔1, isolation profile I1/I2) │
|
|
|
|
|
|
│ - policy kernel (default-deny; existing, unchanged) │
|
|
|
|
|
|
│ - REST + MCP surfaces (existing, unchanged) │
|
|
|
|
|
|
│ - OpenBao fetch on cold start only, short TTL │
|
|
|
|
|
|
│ - audit emission shaped as Kings Guard Immune Observations │
|
|
|
|
|
|
└───────────────────────────┬───────────────────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
OpenBao (tenants/binky/qonto-api)
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
Qonto thirdparty API
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The facade and the scale-to-zero controller are **the same component**.
|
|
|
|
|
|
Building it once satisfies both the cost concern (service is idle most of
|
|
|
|
|
|
the time and should not burn compute) and the security concern (nothing
|
|
|
|
|
|
reachable from the internet is allowed to trigger a cold start of the
|
|
|
|
|
|
credential-holding process without first clearing identity and
|
|
|
|
|
|
authorization checks). This is deliberate: a "wake on any request" facade
|
|
|
|
|
|
without the auth gate would just be a new, free DoS/cost-exhaustion lever.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 3. Identity — replace the interim bearer token with `key-cape`
|
|
|
|
|
|
|
|
|
|
|
|
`docs/mcp-integration.md` already documents today's interim state: a
|
|
|
|
|
|
shared-secret bearer token (`QONTO_ASSISTANT_MCP_TOKEN`) stands in for real
|
|
|
|
|
|
workload/client identity, explicitly because "no OIDC issuer exists in this
|
|
|
|
|
|
fleet yet."
|
|
|
|
|
|
|
|
|
|
|
|
That premise no longer holds for this deployment. `key-cape`
|
|
|
|
|
|
(Authelia + LLDAP + privacyIDEA) is the fleet's real, already-running IAM
|
|
|
|
|
|
Profile issuer — it is SSO today for ops/Temporal UI (`NK-WP-0021`). Before
|
|
|
|
|
|
`qonto-assistant` is internet-facing:
|
|
|
|
|
|
|
|
|
|
|
|
- The facade verifies a `key-cape`-issued IAM Profile token (human operator
|
|
|
|
|
|
session or service/workload identity) on every inbound request, before
|
|
|
|
|
|
any wake decision.
|
|
|
|
|
|
- The shared-secret `QONTO_ASSISTANT_MCP_TOKEN` becomes fixture/local-dev
|
|
|
|
|
|
only (as already documented) and must never be the auth boundary for a
|
|
|
|
|
|
real deployment.
|
|
|
|
|
|
- Actor claims (`X-Actor-ID`, `X-Tenant-ID`, `X-Actor-Lane`, `X-Actor-Scopes`)
|
|
|
|
|
|
stop being self-asserted headers and are instead derived from the
|
|
|
|
|
|
verified token — closing the gap `docs/mcp-integration.md` §Auth model
|
|
|
|
|
|
calls out explicitly ("self-asserted today, not cryptographically bound").
|
|
|
|
|
|
|
|
|
|
|
|
## 4. Authorization — register `finance.qonto.read` in `flex-auth` now
|
|
|
|
|
|
|
|
|
|
|
|
`config.py` already reserves `QONTO_ASSISTANT_REQUIRED_SCOPE` /
|
|
|
|
|
|
`QONTO_ASSISTANT_ENFORCE_SCOPE` for exactly this and ships with enforcement
|
|
|
|
|
|
off. Given internet exposure, this should not wait for "Phase 3" — it is
|
|
|
|
|
|
the actual authorization control point standing between an authenticated
|
|
|
|
|
|
caller and a live bank-data read.
|
|
|
|
|
|
|
|
|
|
|
|
- Register `finance.qonto.read` (and, if useful later, a narrower
|
|
|
|
|
|
`finance.qonto.read.transactions` / `.export`) as a `flex-auth` resource.
|
|
|
|
|
|
- The facade (or the assistant itself, on cold-start request) calls
|
|
|
|
|
|
`flex-auth` for a live decision rather than trusting a cached claim for
|
|
|
|
|
|
this resource class — mirroring the same reasoning `tenant-engine`
|
|
|
|
|
|
applies to itself: *"a stale `VEN` grant surviving a plan cancellation is
|
|
|
|
|
|
not an acceptable risk for money-movement or credential-vending
|
|
|
|
|
|
actions"* — read-only finance visibility deserves the same live-check
|
|
|
|
|
|
discipline, not just write paths.
|
|
|
|
|
|
- Gate on **live tenant capability role** via `tenant-engine`
|
|
|
|
|
|
(`VEN`/`CUS`, non-exclusive) and plan, replacing the current hardcoded
|
|
|
|
|
|
`default_tenant_id="binky"` assumption in `config.py`. A tenant whose
|
|
|
|
|
|
plan lapses should lose read access the same request cycle, not whenever
|
|
|
|
|
|
a cache expires.
|
|
|
|
|
|
|
|
|
|
|
|
## 5. Network exposure — the facade is the only internet-facing thing
|
|
|
|
|
|
|
|
|
|
|
|
`qonto-assistant`'s raw REST/MCP port must never be bound to a
|
|
|
|
|
|
publicly-reachable address, in any deployment. Concretely:
|
|
|
|
|
|
|
|
|
|
|
|
- The Kubernetes `Service`/ingress in front of `qonto-assistant` itself
|
|
|
|
|
|
should remain cluster-internal (`ClusterIP`), matching the pattern
|
|
|
|
|
|
`railiance-rhythm`'s host timers already use for `llm-connect`.
|
|
|
|
|
|
- The facade is the sole component with an external address. It is
|
|
|
|
|
|
deliberately dumb: no policy kernel, no bank credential, no Qonto client
|
|
|
|
|
|
— its only job is auth-gate, wake, proxy, and idle-timeout.
|
|
|
|
|
|
- Failure mode: if `key-cape` or `flex-auth` is unreachable, the facade
|
|
|
|
|
|
**fails closed** (refuses to wake the backend), per Kings Guard principle
|
|
|
|
|
|
6.11 ("fail securely, not blindly") — an outage in the auth path must not
|
|
|
|
|
|
silently become an open-access mode.
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Scale-to-zero — cost control and security control together
|
|
|
|
|
|
|
|
|
|
|
|
- Facade holds the public address; backend `Deployment` runs at `replicas:
|
|
|
|
|
|
0` when idle.
|
|
|
|
|
|
- On an authenticated+authorized request, facade scales the backend to 1,
|
|
|
|
|
|
waits on `GET /v1/health`, then proxies through.
|
|
|
|
|
|
- An idle-timeout watcher (same component or a sidecar) scales back to 0
|
|
|
|
|
|
after a configurable window (proposed default: 10–15 minutes of no
|
|
|
|
|
|
traffic) — tunable per deployment, not hardcoded.
|
|
|
|
|
|
- If the target cluster already runs Knative Serving, this is close to the
|
|
|
|
|
|
built-in Activator + scale-to-zero autoscaler pattern and should reuse it
|
|
|
|
|
|
rather than reinventing one. Otherwise a minimal custom
|
|
|
|
|
|
controller is small and narrow in scope (one Deployment, one Service,
|
|
|
|
|
|
one wake/idle state machine) and should be scoped as its own follow-on
|
|
|
|
|
|
task rather than folded into `qonto-assistant`'s own codebase — see
|
|
|
|
|
|
`QONTO-WP-0004`.
|
|
|
|
|
|
- Security side effect, not just a cost one: because OpenBao secret fetch
|
|
|
|
|
|
already uses a short TTL (`qonto_secret_ttl_seconds`), scale-to-zero
|
|
|
|
|
|
shrinks the bank credential's residency window in process memory to
|
|
|
|
|
|
"only while an authenticated request is actually being served" — smaller
|
|
|
|
|
|
than today's always-on posture, for free.
|
|
|
|
|
|
|
|
|
|
|
|
## 7. Isolation profile
|
|
|
|
|
|
|
|
|
|
|
|
Per `kings-guard`'s isolation-profile classification
|
|
|
|
|
|
(`NetKingdomImmuneArchitecture.md` §11), given:
|
|
|
|
|
|
|
|
|
|
|
|
- tenant-confidential financial data classification,
|
|
|
|
|
|
- internet reachability (via the facade),
|
|
|
|
|
|
- sole custody of a real bank credential,
|
|
|
|
|
|
|
|
|
|
|
|
`qonto-assistant` should run at **I1 Reinforced** at minimum (dedicated
|
|
|
|
|
|
node pool or sandboxed runtime) rather than the shared/logical-isolation
|
|
|
|
|
|
default (I0) other low-risk internal services use. **I2 Dedicated**
|
|
|
|
|
|
(tenant-specific control/compute plane) is worth considering given it is
|
|
|
|
|
|
the single component in the fleet holding this particular credential — the
|
|
|
|
|
|
decision belongs to whoever owns Railiance placement for this workload, not
|
|
|
|
|
|
to this repo alone.
|
|
|
|
|
|
|
|
|
|
|
|
## 8. Recovery
|
|
|
|
|
|
|
|
|
|
|
|
`qonto-assistant` already has the properties Kings Guard's "recovery is a
|
|
|
|
|
|
first-class capability" principle (AD-006) asks for, largely as a side
|
|
|
|
|
|
effect of how it was built, not extra work:
|
|
|
|
|
|
|
|
|
|
|
|
- **Stateless** — no persisted secrets or session state; redeployable at
|
|
|
|
|
|
any time.
|
|
|
|
|
|
- **Short-TTL credential** — OpenBao lease expires quickly; a fresh pod
|
|
|
|
|
|
fetches its own, it never inherits a stale one.
|
|
|
|
|
|
- **Cheap reconstitution** — "kill the pod, let the facade cold-start a
|
|
|
|
|
|
fresh one on the next legitimate request" is a complete, low-cost
|
|
|
|
|
|
recovery procedure with no data-loss risk (`maximum_data_loss: PT0S`
|
|
|
|
|
|
applies trivially since there is no persisted data).
|
|
|
|
|
|
|
2026-09-05 01:39:48 +02:00
|
|
|
|
## 9. King's Guard observation mapping
|
Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
|
2026-09-05 01:39:48 +02:00
|
|
|
|
King's Guard has observed the structured allow/deny stream. The source keeps
|
|
|
|
|
|
ownership of its genome and emission claims; the observer independently checks
|
|
|
|
|
|
the records and completeness evidence it receives.
|
Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
|
|
|
|
|
|
### 9.1 Security Genome record
|
|
|
|
|
|
|
|
|
|
|
|
See `specs/security-genome.yaml` for the concrete record following
|
|
|
|
|
|
`NetKingdomImmuneArchitecture.md` §9.1's schema — declared purpose,
|
|
|
|
|
|
capabilities provided/consumed, expected egress (Qonto API + OpenBao only,
|
|
|
|
|
|
nothing else), data classification, and recovery expectations.
|
|
|
|
|
|
|
|
|
|
|
|
### 9.2 Audit stream is already observation-shaped
|
|
|
|
|
|
|
2026-09-05 01:39:48 +02:00
|
|
|
|
`AuditLogger`'s event shape includes actor, capability, decision, deny reason,
|
|
|
|
|
|
latency, upstream HTTP status, policy version, identity binding, and egress
|
|
|
|
|
|
destination (see `tests/test_audit.py`, `tests/test_audit_parity.py`). Every
|
|
|
|
|
|
record also carries a process-instance id and monotonic stream sequence.
|
|
|
|
|
|
|
|
|
|
|
|
The source-owned declaration in `specs/audit-emission-cadence.yaml` classifies
|
|
|
|
|
|
`audit.deny` as load-bearing and low-volume. A startup heartbeat, periodic
|
|
|
|
|
|
heartbeat (24 hours by default), and best-effort shutdown heartbeat publish
|
|
|
|
|
|
cumulative and window transition counts. Consumers compare those counts and
|
|
|
|
|
|
sequence continuity with observed records. `/v1/audit/reconciliation` exposes
|
|
|
|
|
|
the same non-secret current source counts for an authenticated diagnostic read.
|
Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
|
|
|
|
|
|
### 9.3 A concrete, actionable-today signal
|
|
|
|
|
|
|
|
|
|
|
|
This does not require any Kings Guard component: repeated
|
|
|
|
|
|
`arg_constraint` or `credential_exfil` deny reasons from the same actor
|
|
|
|
|
|
within a short window is a real, current signal. It should trip a
|
2026-09-05 01:39:48 +02:00
|
|
|
|
tightened rate limit or a temporary lockout for that actor now. The current
|
|
|
|
|
|
`DenyEscalationTracker` branches directly on the same decision path that emits
|
|
|
|
|
|
the audit event; it does not consume the audit stream. This is a Fast Local Loop
|
|
|
|
|
|
(`NetKingdomImmuneArchitecture.md` §14.1) response that does not need to wait
|
|
|
|
|
|
for any future component, while the stream remains load-bearing for estate
|
|
|
|
|
|
observation rather than for the local lockout.
|
Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 10. What this repo can do unilaterally vs. what it depends on
|
|
|
|
|
|
|
|
|
|
|
|
| Item | Owner | Status |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| Security Genome record | `qonto-assistant` | can ship now |
|
|
|
|
|
|
| Audit-stream shape review against Immune Observation contract | `qonto-assistant` | can ship now |
|
|
|
|
|
|
| Actor lockout on repeated deny signals | `qonto-assistant` | can ship now |
|
2026-09-05 01:39:48 +02:00
|
|
|
|
| `key-cape` token verification in place of bearer token | `qonto-assistant` + `key-cape` | verifier shipped; live enforcement is runtime-configured |
|
|
|
|
|
|
| `finance.qonto.read` resource + live decision call | `qonto-assistant` + `flex-auth` | client and policy shipped; live use is runtime-configured |
|
|
|
|
|
|
| Live tenant-role gate | `qonto-assistant` + `tenant-engine` | client shipped; live use requires a reachable deployment URL |
|
Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
| Facade / scale-to-zero activator | new component (home TBD — Railiance or a dedicated repo) | design only so far |
|
|
|
|
|
|
| I1/I2 isolation placement on `railiance01` | Railiance | needs a placement decision |
|
2026-09-05 01:39:48 +02:00
|
|
|
|
| Audit-stream observation and completeness acceptance | `kings-guard` | consuming events; cadence/reconciliation acceptance pending |
|
Add SecurityPractice.md, Security Genome record, and deny-escalation lockout
Design doc for hardening qonto-assistant before deployment to
railiance01: this is the first fleet service that must be
internet-reachable (external harness clients, not just in-cluster
jobs) while holding a real bank credential. Covers identity (key-cape
in place of the interim bearer token), authorization (finance.qonto.read
in flex-auth + tenant-engine capability roles instead of the
hardcoded default_tenant_id), network exposure (facade-only internet
address), isolation profile, and a Kings Guard mapping (the existing
audit stream is already Immune-Observation-shaped; nothing to rebuild
later).
Ships one concrete, dependency-free piece of that design now:
DenyEscalationTracker locks out an actor who repeatedly triggers
arg_constraint/credential_exfil denies within a short window, closing
the gap where a probing client could retry indefinitely at whatever
rate the existing rate limiter otherwise allows. Wired through
CapabilityService, on by default, configurable via
QONTO_DENY_ESCALATION_* env vars. Ordinary denies (authz_denied,
tenant_scope) never count toward it.
Also adds specs/security-genome.yaml (kings-guard's genome-record
shape, populated now so no rework is needed once a consumer exists).
Verified: pytest -> 39 passed (8 new); REST and MCP smoke scripts both
pass against fixtures; compileall clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 22:59:06 +02:00
|
|
|
|
|
|
|
|
|
|
Tracked as `QONTO-WP-0004` in this repo, with the `kings-guard`-owned
|
|
|
|
|
|
portion tracked as an intake against `KG-WP-0002` (pilot-lane selection).
|