qonto-assistant/workplans/QONTO-WP-0002-policy-kernel-and-rest.md
tegwick ca12843013 Complete Phase 1: policy kernel, REST service, and local smoke tooling
Implements QONTO-WP-0002 (policy-gated Qonto REST service with audit
logging, rate limiting, and credential handling) and the ADHOC-2026-07-21
follow-up (fixture-backed local smoke mode, repo classification metadata).
Marks QONTO-WP-0001/0002 and the ad-hoc workplan finished, and regenerates
WORK-RECORDS.md and the ADHOC workplan's state_hub_workstream_id via
fix-consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 21:21:05 +02:00

5.2 KiB

id type title domain repo status owner topic_slug created updated state_hub_workstream_id
QONTO-WP-0002 workplan Phase 1 — policy kernel and read-only REST infotech qonto-assistant finished codex the-custodian 2026-07-21 2026-07-21 1540afc2-219e-4d95-96f5-4b45fc6a7aaa

Phase 1 — policy kernel and read-only REST

Execute Phase 1 of specs/ArchitectureBlueprint.md: a service skeleton with a protocol-neutral capability core and shared decide(request, claims) → Allow|Deny policy kernel plus a minimal REST surface for org/accounts/transactions/snapshot. No MCP yet (Phase 2). No spend or volume-cost tools — default deny. Baseline guardrails ship in this phase: bounded pagination/timeouts, basic rate limiting, bounded concurrency, and redaction tests.

Depends on: live OpenBao path tenants/binky/qonto-api (BINKY-WP-0005 / CCR-2026-0008) — already provisioned.

Task: Choose runtime skeleton and layout

id: QONTO-WP-0002-T01
status: done
priority: high
state_hub_task_id: "f9e129f3-5bd4-43e1-b7a0-281e4d3dec2a"

Pick implementation stack (recommendation: Python 3.12 + FastAPI unless a fleet standard dictates otherwise). Scaffold package layout, pyproject.toml (or equivalent), Makefile targets (test, lint, run), and document commands in AGENTS.md / complete QONTO-WP-0001-T02.

Done when: make test (or documented equivalent) runs an empty/smoke suite; layout matches blueprint components (protocol adapters, capability core, policy, qonto client, api, audit).

Task: Policy kernel — default-deny no-spend / no-volume-cost

id: QONTO-WP-0002-T02
status: done
priority: high
state_hub_task_id: "552ff651-dc66-4e65-97fe-3ec26652bbdd"

Implement declarative policy (YAML or equivalent) + pure decision function:

  • default deny
  • allow only v1 read capability ids (org_summary, list_transactions, cost_run_rate_hints, snapshot_bundle)
  • hard deny classes: spend, volume_cost, credential_exfil
  • stable deny reasons for authz, tenant scope, argument constraint, and credential exfil cases
  • unit tests: allow known reads; deny transfer/card/invoice-shaped tools and suspicious args even if somehow invoked

Done when: policy tests pass in CI/local; no network required.

Task: Qonto REST client (credential inject, no secret log)

id: QONTO-WP-0002-T03
status: done
priority: high
state_hub_task_id: "be3aa7b6-f28c-4436-bd5d-d6940de6c2ce"

Implement thirdparty client using Authorization: login:key (fields API_USER/API_KEY from env or OpenBao fetch helper). Map:

  • organization + bank accounts
  • paginated transactions with hard caps
  • bounded timeouts and conservative retry behavior
  • never log Authorization or key material

Support env inject for tests (QONTO_API_KEY/QONTO_ORGANIZATION_ID or API_KEY/API_USER) and document OpenBao fetch for operators. Production path fetches through a dedicated assistant runtime role only.

Done when: unit tests with mocked HTTP; optional live smoke behind a flag.

Task: REST API surface

id: QONTO-WP-0002-T04
status: done
priority: high
state_hub_task_id: "678b0b26-15af-4037-849f-d24d320588ac"

Expose JSON endpoints that all run through the policy kernel:

Method Path Capability
GET /v1/health no bank call
GET /v1/accounts org_summary
GET /v1/transactions list_transactions (capped)
GET /v1/snapshot snapshot_bundle (composed allowed reads)

Done when: OpenAPI or documented curl examples; integration test with mock client; deny paths return 403 with stable error code; route-to-capability mapping is explicit and shared with future MCP.

Task: Audit metadata (no secrets)

id: QONTO-WP-0002-T05
status: done
priority: medium
state_hub_task_id: "4a42dff1-1281-4cc1-ba6a-24702bce7dc9"

Log or emit structured audit events: actor (if present), capability, decision, deny_reason, latency, upstream HTTP status, and policy version. Never secret fields. Prefer structured stdout JSON (or equivalent sink) on the hot path; State Hub progress stays optional roll-up evidence for dogfood runs, not per-call audit storage.

Done when: tests assert secrets absent from log lines for a sample allow/deny.

Task: Operator runbook + CostRunRate refresh path

id: QONTO-WP-0002-T06
status: done
priority: medium
state_hub_task_id: "7b7ca0f7-f523-473e-b3f6-fe0564f54ed5"

Document how binky-control refreshes finance/CostRunRate.md via GET /v1/snapshot (or equivalent) with OpenBao-backed service start. Link from README. Optional thin script under scripts/.

Done when: runbook in docs/ or README section; dry-run instructions without pasting keys into chat.

Task: Closure review

id: QONTO-WP-0002-T07
status: done
priority: low
state_hub_task_id: "9b99ba05-99f2-4624-9044-89bf37055434"

Done 2026-07-21: Phase 1 landed in src/qonto_assistant/ with a FastAPI-based REST service, policy YAML, Qonto client, audit layer, rate limits, concurrency bounds, and operator runbook. Verified with PYTHONPATH=src ../state-hub/.venv/bin/python -m pytest (12 passed) plus python3 -m compileall src tests. Phase 2 seed remains the MCP surface on the same capability core and policy engine. Run statehub fix-consistency.