qonto-assistant/workplans/QONTO-WP-0002-policy-kernel-and-rest.md

156 lines
5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: QONTO-WP-0002
type: workplan
title: "Phase 1 — policy kernel and read-only REST"
domain: infotech
repo: qonto-assistant
status: active
owner: codex
topic_slug: the-custodian
created: "2026-07-21"
updated: "2026-07-21"
state_hub_workstream_id: "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
```task
id: QONTO-WP-0002-T01
status: progress
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
```task
id: QONTO-WP-0002-T02
status: progress
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)
```task
id: QONTO-WP-0002-T03
status: progress
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
```task
id: QONTO-WP-0002-T04
status: progress
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)
```task
id: QONTO-WP-0002-T05
status: progress
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
```task
id: QONTO-WP-0002-T06
status: progress
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
```task
id: QONTO-WP-0002-T07
status: todo
priority: low
state_hub_task_id: "9b99ba05-99f2-4624-9044-89bf37055434"
```
Mark workplan finished when T01T06 done; note Phase 2 seed (MCP surface for
all harnesses) in closure. Run `statehub fix-consistency`.