docs(P00): grill — v1.28 adversarial review (PROCEED 0.76, 3 critical + 16 tracked conditions applied)
---ci--- project: acdl phase: 0 milestone: v1.28 status: grill ---/ci---
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"phase": 0,
|
"phase": 0,
|
||||||
"stage": "plan",
|
"stage": "grill",
|
||||||
"milestone": "v1.28",
|
"milestone": "v1.28",
|
||||||
"phase_role": "pre_execution",
|
"phase_role": "pre_execution",
|
||||||
"attempts": 0,
|
"attempts": 0,
|
||||||
"updated_at": "2026-08-19T20:40:00Z",
|
"updated_at": "2026-08-19T20:55:00Z",
|
||||||
"project": "acdl",
|
"project": "acdl",
|
||||||
"projects": ["acdl", "nova-blockchain-exchange"],
|
"projects": ["acdl", "nova-blockchain-exchange"],
|
||||||
"active_milestone": "v1.28",
|
"active_milestone": "v1.28",
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
"previous_milestone": {"milestone": "v1.27", "tag": "v1.26.3", "status": "complete"},
|
"previous_milestone": {"milestone": "v1.27", "tag": "v1.26.3", "status": "complete"},
|
||||||
"decisions": ["D-226", "D-227", "D-228", "D-229", "D-230", "D-231"],
|
"decisions": ["D-226", "D-227", "D-228", "D-229", "D-230", "D-231"],
|
||||||
"personas": ["backend-engineer", "security-engineer", "cli-engineer", "lead-developer"],
|
"personas": ["backend-engineer", "security-engineer", "cli-engineer", "lead-developer"],
|
||||||
"phases_planned": 7,
|
"phases_planned": 6,
|
||||||
"execution_phases": [
|
"execution_phases": [
|
||||||
{"phase": 1, "name": "cli-substrate", "reqs": ["REQ-323..328"], "caps": ["CAP-033", "CAP-034", "CAP-035"], "tag": "v1.27.1"},
|
{"phase": 1, "name": "cli-substrate", "reqs": ["REQ-323..328"], "caps": ["CAP-033", "CAP-034", "CAP-035"], "tag": "v1.27.1"},
|
||||||
{"phase": 2, "name": "lambda-packaging", "reqs": ["REQ-329..331"], "tag": "v1.27.2"},
|
{"phase": 2, "name": "lambda-packaging", "reqs": ["REQ-329..332"], "tag": "v1.27.2"},
|
||||||
{"phase": 3, "name": "idp-auth", "reqs": ["REQ-333..335"], "caps": ["CAP-036"], "tag": "v1.27.3"},
|
{"phase": 3, "name": "idp-auth", "reqs": ["REQ-333..335"], "caps": ["CAP-036"], "tag": "v1.27.3"},
|
||||||
{"phase": 4, "name": "token-vend-pat", "reqs": ["REQ-336..344"], "caps": ["CAP-037", "CAP-038"], "tag": "v1.27.4"},
|
{"phase": 4, "name": "token-vend-pat", "reqs": ["REQ-336..344", "REQ-340..341"], "caps": ["CAP-037", "CAP-038"], "tag": "v1.27.4"},
|
||||||
{"phase": 5, "name": "idp-setup", "reqs": ["REQ-340..341"], "tag": "v1.27.5"},
|
{"phase": 5, "name": "docs-integration", "reqs": ["REQ-345..351"], "tag": "v1.27.5"},
|
||||||
{"phase": 6, "name": "docs-integration", "reqs": ["REQ-345..351"], "tag": "v1.27.6"},
|
{"phase": 6, "name": "final-review-ship", "reqs": ["REQ-352..353"], "tag": "v1.27.6"}
|
||||||
{"phase": 7, "name": "final-review-ship", "reqs": ["REQ-352..353"], "tag": "v1.27.7"}
|
|
||||||
],
|
],
|
||||||
"notes": "v1.28 PLAN complete. 7 phases (P1..P6 execution + P7 final). 31 REQs mapped to waves+tasks. 6 CAPs mapped to gate rules. MVP/UX sections authored (User-Facing Surface, Happy Path, UX Acceptance Criteria). Highest risk: P4 W1 kj-binary spike. Next: GRILL."
|
"grill": {"verdict": "PROCEED-WITH-CONDITIONS", "confidence": 0.76, "critical_conditions": 3, "tracked_conditions": 16, "escalations": 0},
|
||||||
|
"notes": "v1.28 GRILL complete. 9 axes reviewed; all PROCEED-WITH-CONDITIONS (>=0.70). 3 critical fixes applied (ABAC fail-closed C-6.1, JWS KDF C-5.2, traceability drift C-9.1) + 16 tracked conditions. P5 folded into P4 Wave 8 (C-2.1) -> 6 execution phases. Cost envelope ~$9/mo. Next: MVP/UX CHECK -> SHIP."
|
||||||
}
|
}
|
||||||
+98
-129
@@ -1,141 +1,110 @@
|
|||||||
# GRILL — v1.27 PO State Catalog & Ciagent Compression
|
# GRILL — v1.28 CLI Canonicalization + Identity Layer
|
||||||
|
|
||||||
> Adversarial review of the v1.27 SPECIFY + CLARIFY + RESEARCH + PLAN.
|
> Adversarial review of the v1.28 SPECIFY + CLARIFY + RESEARCH + PLAN.
|
||||||
> The grill red-teams the proposal across feasibility, scope, and the
|
> Griller: ci-griller subagent. Autonomy: full. All 9 axes reviewed;
|
||||||
> compression-loss claims. Each challenge gets a binding verdict
|
> every claim verified against the live codebase.
|
||||||
> (PROCEED / REVISE / ESCALATE). Autonomy: full.
|
|
||||||
|
|
||||||
## Verdict: PROCEED (0.88) — 0 escalations, 1 revision
|
|
||||||
|
|
||||||
The milestone is feasible, scoped, and the compression is lossless. One
|
|
||||||
binding revision (G-Q2) refines the archive list; already captured in
|
|
||||||
PLAN. No work is blocked.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Challenges
|
## Overall verdict: **PROCEED-WITH-CONDITIONS** · Confidence 0.76
|
||||||
|
|
||||||
### G-Q1 — Is archiving AUTONOMY_THESIS.md + COST.md a context loss?
|
The plan is fundamentally sound — architecture correct, re-mapping
|
||||||
|
clean (no ID collisions), technical depth accurate (DER→raw, strong-
|
||||||
|
read revocation, stdin TTY), highest-risk item (kj binary) has a
|
||||||
|
Fargate fallback. Not unfeasible, not over-scoped beyond an agent-driven
|
||||||
|
repo's capacity, not security-broken by design.
|
||||||
|
|
||||||
**Challenge:** `AUTONOMY_THESIS.md` is the "autonomy in operations;
|
**3 critical conditions (must-fix before P1) + 16 tracked conditions.**
|
||||||
human at stage gates" thesis — the defensibility brief. `COST.md` is
|
No escalations (all axes ≥ 0.70 confidence).
|
||||||
the only AWS cost record. Archiving both moves them out of active
|
|
||||||
context. Does this lose load-bearing content?
|
|
||||||
|
|
||||||
**Verdict:** PROCEED (confidence 0.90).
|
|
||||||
- `AUTONOMY_THESIS.md` (65 lines, "Last refined: v1.21") is fully
|
|
||||||
folded into `NORTH_STAR.md` Vision (lines 17–22: "infrastructure
|
|
||||||
operations become visible... human attestation remains required at
|
|
||||||
stage gates") + Anti-Goals #2 ("Not a system that removes humans from
|
|
||||||
accountability"). The thesis is the source; NORTH_STAR is the
|
|
||||||
authoritative durable copy. Archive preserves the v1.21 refinement;
|
|
||||||
active context reads NORTH_STAR.
|
|
||||||
- `COST.md` (106 lines, dated 2026-07-29, "v1.0 → v1.14") predates the
|
|
||||||
v1.26 live pilot. The v1.26 live apply (ECS + ALB + DynamoDB + S3)
|
|
||||||
incurred real costs this snapshot doesn't reflect. Archiving it is
|
|
||||||
honest — a stale cost record misleads. STATE.md Domain 7 notes cost
|
|
||||||
tracking as a capability (pre-apply Infracost grounded; actual-spend
|
|
||||||
CUR deferred D-096). A future cost milestone writes a fresh report.
|
|
||||||
No revision needed.
|
|
||||||
|
|
||||||
### G-Q2 — Does the archive list include the v1.27 P0 pre-execution files by mistake?
|
|
||||||
|
|
||||||
**Challenge:** D-219 (user-confirmed) says "archive all 4 pre-execution
|
|
||||||
artifacts" (CLARIFY/GRILL/IDEATE/RESEARCH). But P0 already overwrote
|
|
||||||
them with v1.27 content. Archiving the v1.27 versions at v1.27 P1 would
|
|
||||||
lose the v1.27 pre-execution narrative (the decisions D-214..D-225, the
|
|
||||||
research inventory, this grill). Is the archive list wrong?
|
|
||||||
|
|
||||||
**Verdict:** REVISE (confidence 0.92). This is a real ambiguity in the
|
|
||||||
plan. The user's D-219 decision was made *before* P0 overwrote the
|
|
||||||
files; the intent was to archive the *v1.26* pre-execution record. The
|
|
||||||
v1.26-era content is preserved in git history (the pre-P0 commits) —
|
|
||||||
the archive directory is not the only preservation layer. PLAN Task 2.1
|
|
||||||
already self-corrected: the final archive list is **7 platform files +
|
|
||||||
1 consumer file = 8 files**, excluding the 4 pre-execution files. The 4
|
|
||||||
v1.27 P0 versions stay active through v1.27; they archive at v1.28 P1
|
|
||||||
if v1.28 happens. The archive README notes the v1.26 pre-execution
|
|
||||||
record is in git history. No further revision needed — the plan self-
|
|
||||||
corrected.
|
|
||||||
|
|
||||||
### G-Q3 — Is the STATE.md backfill accurate enough to be the PO's source of truth?
|
|
||||||
|
|
||||||
**Challenge:** STATE.md has 36 capability rows across 10 domains,
|
|
||||||
backfilled from 8 sources. The PO will read this before writing new
|
|
||||||
REQs. If a row is inaccurate (wrong shipped tag, wrong file path,
|
|
||||||
wrong controlling REQ), the PO could re-spec an existing capability or
|
|
||||||
cite a stale invariant. Is the backfill accurate?
|
|
||||||
|
|
||||||
**Verdict:** PROCEED (confidence 0.85). The backfill sources are
|
|
||||||
authoritative: `core/regression_verify.py` (the machine CAP-NNN
|
|
||||||
registry), `modules/registry.json` (the live module catalog),
|
|
||||||
`REQUIREMENTS.md` traceability (the REQ→phase→status record),
|
|
||||||
`CHECKPOINT.json` (shipped tags), `git log` (file paths). The
|
|
||||||
citations are direct (each row cites the controlling REQ + decision
|
|
||||||
ID). The 11 invariants are distilled from PROJECT.md load-bearing
|
|
||||||
decisions D-034..D-072 + W1..BA + Q1.3. The accuracy risk is
|
|
||||||
mitigated by P1 Wave 1 (verify STATE.md against sources before
|
|
||||||
archive). No revision needed — the verification step is in the plan.
|
|
||||||
|
|
||||||
### G-Q4 — Does the NFR purity gate (zero `feat:` commits) hold for v1.27?
|
|
||||||
|
|
||||||
**Challenge:** v1.27 authors STATE.md (a new file). Is authoring a new
|
|
||||||
catalog file a `feat:` (feature) that breaks the NFR purity gate?
|
|
||||||
|
|
||||||
**Verdict:** PROCEED (confidence 0.92). D-220 (CLARIFY) resolved this:
|
|
||||||
STATE.md is documentation (a catalog of *existing* capabilities), not a
|
|
||||||
new platform capability. The archive moves are `chore:` (file
|
|
||||||
relocation, lossless). No code, no schema, no platform behavior
|
|
||||||
change. The NFR purity gate (zero `feat:` commits) holds. All v1.27
|
|
||||||
commits use `docs(P0N):` or `chore(P01):` prefixes. No revision
|
|
||||||
needed.
|
|
||||||
|
|
||||||
### G-Q5 — Does fixing PROJECT.md phase-status in P2 create a P0/P1 audit inconsistency?
|
|
||||||
|
|
||||||
**Challenge:** The PROJECT.md phase-status block shows P3/P4/P5 as
|
|
||||||
"pending" (the bug flagged in the prior conversation). P0 + P1 ship
|
|
||||||
with the bug still present (the fix is P2). Does the P0/P1 audit see
|
|
||||||
the inconsistency?
|
|
||||||
|
|
||||||
**Verdict:** PROCEED (confidence 0.86). The bug is pre-existing
|
|
||||||
(it predates v1.27; it was the trigger for the prior conversation).
|
|
||||||
P0/P1 audits check the *v1.27* commits against the `.ciagent/` state,
|
|
||||||
not the pre-existing PROJECT.md drift. The P2 fix is the correction;
|
|
||||||
the P3 audit verifies the fix landed. The intermediate state (P0/P1
|
|
||||||
with the bug present) is honest — the bug is documented in the v1.27
|
|
||||||
PLAN + the prior conversation, and the fix is scheduled. No revision
|
|
||||||
needed — the phasing is intentional (D-225: P1 additive, P2
|
|
||||||
correction).
|
|
||||||
|
|
||||||
### G-Q6 — Is the milestone scoped too small (3 phases, 8 archive moves)?
|
|
||||||
|
|
||||||
**Challenge:** v1.27 is a small milestone (3 phases, ~15 file
|
|
||||||
operations, no code). Is it worth a milestone, or should it be a
|
|
||||||
patch on v1.26?
|
|
||||||
|
|
||||||
**Verdict:** PROCEED (confidence 0.88). v1.27 is not a patch on v1.26
|
|
||||||
— v1.26 is shipped (`v1.25.5`, merged to main, milestone complete).
|
|
||||||
The work is a new milestone by definition. The size is appropriate:
|
|
||||||
STATE.md is a durable PO-facing artifact (loaded every ci-run going
|
|
||||||
forward); the compression reduces active context by ~26%; the
|
|
||||||
ship-discipline wiring affects every future milestone ship. Small but
|
|
||||||
high-leverage. No revision needed.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Summary
|
## Axis verdicts
|
||||||
|
|
||||||
6 challenges; 0 escalations; 1 binding revision (G-Q2, already
|
| Axis | Verdict | Confidence | Critical condition |
|
||||||
captured in PLAN Task 2.1). Overall verdict: PROCEED (confidence
|
|------|---------|-----------|-------------------|
|
||||||
0.88).
|
| §1 Feasibility | PROCEED-WITH-CONDITIONS | 0.82 | C-1.1 KMS asym verify; C-1.2 Argon2 fail-closed test |
|
||||||
|
| §2 Scope | PROCEED-WITH-CONDITIONS | 0.74 | C-2.1 fold P5 into P4; C-2.2 P4 overload |
|
||||||
|
| §3 Cost | PROCEED-WITH-CONDITIONS | 0.70 | C-3.1 cost estimate; C-3.2 CodeArtifact P1 task |
|
||||||
|
| §4 Schedule | PROCEED-WITH-CONDITIONS | 0.76 | C-4.1 P4 critical path; C-4.2 per-phase exit |
|
||||||
|
| §5 Technical Depth | PROCEED-WITH-CONDITIONS | 0.80 | C-5.1 ABAC shape; **C-5.2 JWS KDF** |
|
||||||
|
| §6 Operational Readiness | PROCEED-WITH-CONDITIONS | 0.72 | **C-6.1 ABAC fail-closed**; C-6.2 threat model; C-6.3 ops guide |
|
||||||
|
| §7 Security Posture | PROCEED-WITH-CONDITIONS | 0.73 | **C-7.1 ABAC fail-closed**; C-7.2 Argon2 params; C-7.3 cred file |
|
||||||
|
| §8 Dependency Risk | PROCEED-WITH-CONDITIONS | 0.83 | C-8.1 CodeArtifact P1; C-8.2 pin kj version |
|
||||||
|
| §9 Re-mapping Integrity | PROCEED-WITH-CONDITIONS | 0.84 | **C-9.1 traceability fix**; C-9.2 INV audit |
|
||||||
|
|
||||||
**Binding revisions:**
|
---
|
||||||
- **G-Q2:** Archive list refined to 7 platform + 1 consumer = 8 files.
|
|
||||||
The 4 pre-execution files (CLARIFY/GRILL/IDEATE/RESEARCH) stay active
|
|
||||||
through v1.27 (they hold the v1.27 P0 content); the v1.26-era content
|
|
||||||
is in git history. Already in PLAN.
|
|
||||||
|
|
||||||
**No work is blocked.** The milestone is feasible, scoped, the
|
## Critical conditions (the 3 must-fix-before-P1)
|
||||||
compression is lossless (archive + git history), the STATE.md backfill
|
|
||||||
is source-grounded with a verification step, the NFR purity holds, and
|
### 🔴 C-6.1 / C-7.1 — ABAC fail-closed
|
||||||
the phasing (P1 additive, P2 correction, P3 ship) is sound.
|
The token-vend Lambda's behavior on `kj` absence/error is unspecified.
|
||||||
|
Without fail-closed, INV-17 is documentation, not a runtime guarantee —
|
||||||
|
a `kj` load failure would bypass the ABAC gate (every PAT gets a token).
|
||||||
|
**Fix applied to PLAN.md P4 Wave 4 Task 4.1:** "If
|
||||||
|
`KyvernoJsonEngine.is_configured()` returns false or `evaluate()`
|
||||||
|
raises, return 403 + audit `token.vend.denied` (reason:
|
||||||
|
`abac_eval_failed`). Never fail open. Test: `tests/test_abac_fail_closed.py`."
|
||||||
|
|
||||||
|
### 🔴 C-5.2 — JWS-from-PAT key derivation
|
||||||
|
REQ-332's AC ("public key derivable from the PAT") is unimplementable
|
||||||
|
without a specified KDF. A PAT is a JWT, not a keypair.
|
||||||
|
**Fix applied to PLAN.md P2 Wave 2 Task 2.3 + REQ-332 AC:** the JWS
|
||||||
|
uses HMAC-SHA256 with a key derived via
|
||||||
|
`HKDF-SHA256(PAT_bytes, salt='nova-local-attestation', info='jws-signing-key')`
|
||||||
|
→ 32-byte symmetric key. The "public key derivable" AC is re-interpreted:
|
||||||
|
the *verification key* is derived from the PAT via the same KDF (the
|
||||||
|
PAT is the shared secret). This is a symmetric scheme, not asymmetric.
|
||||||
|
|
||||||
|
### 🔴 C-9.1 — Traceability drift
|
||||||
|
REQUIREMENTS.md §v1.28 traceability table mapped 16 REQs to P2;
|
||||||
|
PLAN.md splits them across P2/P3/P4/P5/P6. **Fix applied to
|
||||||
|
REQUIREMENTS.md** — traceability table updated to match PLAN.md phase
|
||||||
|
structure.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tracked conditions (16 — applied to PLAN.md as amendments)
|
||||||
|
|
||||||
|
- **C-1.1** KMS asymmetric key verification before P4 Wave 3 (one
|
||||||
|
`aws kms create-key --key-spec ECC_NIST_P256` call).
|
||||||
|
- **C-1.2** Argon2 fail-closed test in P3 Wave 2 (Lambda returns 503
|
||||||
|
on `ImportError`, not a crash or pure-Python hash).
|
||||||
|
- **C-2.1** Fold P5 (idp-setup) into P4 as P4 Wave 8 → **reduces to 6
|
||||||
|
execution phases** (P1..P6, P7 = final). Applied.
|
||||||
|
- **C-2.2** P4 is a double-length phase; acknowledged in P4 header.
|
||||||
|
- **C-3.1** Cost envelope subsection added to PLAN.md.
|
||||||
|
- **C-3.2 / C-8.1** CodeArtifact provisioning = P1 Wave 0 task with
|
||||||
|
binary go/no-go gate; Gitea wheel index fallback documented.
|
||||||
|
- **C-4.1** P4 flagged as critical-path phase (kj spike = highest-
|
||||||
|
probability schedule slip; Fargate = +1 week).
|
||||||
|
- **C-4.2** Per-phase exit criteria added to PLAN.md.
|
||||||
|
- **C-5.1** `requested_claims` = list of claim names (the policy
|
||||||
|
asserts the subject is *allowed* to request those claims).
|
||||||
|
- **C-6.2** Threat model (REQ-347) adds: JWKS DDoS surface, PAT theft
|
||||||
|
+ max TTL (≤24h dev, ≤1h service-account), ABAC fail-closed,
|
||||||
|
INV-18..21 compression audit.
|
||||||
|
- **C-6.3** Operator guide (REQ-345) adds: KMS rotation, layer update,
|
||||||
|
PITR restore, emergency PAT revocation.
|
||||||
|
- **C-7.2** Argon2id parameters: t=3, m=65536 KiB, p=1 (OWASP min).
|
||||||
|
- **C-7.3** `~/.nova/credentials.json` stores OIDC token + PAT metadata
|
||||||
|
(jti, exp, type), NOT the raw PAT.
|
||||||
|
- **C-8.2** `kj` pinned to a specific release + SHA256 recorded.
|
||||||
|
- **C-9.2** Threat model includes INV-18..21 compression audit
|
||||||
|
(verify spec's attestation invariant semantics are captured by
|
||||||
|
INV-15/16/17 + REQ-332).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Escalations
|
||||||
|
|
||||||
|
None. All 9 axes resolved at confidence ≥ 0.70. No human escalation
|
||||||
|
required (full autonomy).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Grill complete
|
||||||
|
|
||||||
|
The plan proceeds with the 3 critical fixes and 16 tracked conditions
|
||||||
|
applied to PLAN.md + REQUIREMENTS.md. The binding decisions above are
|
||||||
|
the authoritative grill record. Next: MVP/UX CHECK → SHIP phase 0.
|
||||||
+135
-46
@@ -8,7 +8,11 @@
|
|||||||
> `phase/00-pre-execution`, `phase/01-cli-substrate`,
|
> `phase/00-pre-execution`, `phase/01-cli-substrate`,
|
||||||
> `phase/02-lambda-packaging`, `phase/03-idp-auth`,
|
> `phase/02-lambda-packaging`, `phase/03-idp-auth`,
|
||||||
> `phase/04-token-vend-pat`, `phase/05-docs-integration`,
|
> `phase/04-token-vend-pat`, `phase/05-docs-integration`,
|
||||||
> `phase/06-capability-gate`, `phase/07-final-review-ship`.
|
> `phase/06-final-review-ship`.
|
||||||
|
>
|
||||||
|
> **Tags:** `v1.27.0` (P0) → `v1.27.1..v1.27.5` (P1..P5) →
|
||||||
|
> `v1.27.6` (P6 final = milestone release). 6 execution phases
|
||||||
|
> (P5 idp-setup folded into P4 Wave 8 per grill C-2.1).
|
||||||
|
|
||||||
## Milestone goal
|
## Milestone goal
|
||||||
|
|
||||||
@@ -37,7 +41,15 @@ cli-action` composite action; `core/mode_resolver.py`; audit emission
|
|||||||
with `mode` + `selection_reason`. The CLI is installable and every
|
with `mode` + `selection_reason`. The CLI is installable and every
|
||||||
`core/` module is reachable.
|
`core/` module is reachable.
|
||||||
|
|
||||||
**Capability gate target:** CAP-033 (surface), CAP-034 (delegation).
|
**Exit criterion:** CAP-033 + CAP-034 + CAP-035 Verified + all REQ-323..328
|
||||||
|
tests pass. CodeArtifact provisioned (Wave 0 gate).
|
||||||
|
|
||||||
|
#### Wave 0 — CodeArtifact provisioning (backend-engineer) [C-3.2/C-8.1]
|
||||||
|
- **Task 0.1** (backend-engineer): provision CodeArtifact domain
|
||||||
|
(`nova`) + repository (`nova-pypi`) in `581513795199`. Verify
|
||||||
|
`codeartifact:*` IAM grant on `nova-spike-runner`. **Binary go/no-go
|
||||||
|
gate for Wave 4.** If fail: activate Gitea wheel index fallback
|
||||||
|
(CLARIFY assumption #1) and document in PLAN.md.
|
||||||
|
|
||||||
#### Wave 1 — pyproject + entry point (cli-engineer)
|
#### Wave 1 — pyproject + entry point (cli-engineer)
|
||||||
- **Task 1.1** (cli-engineer): `pyproject.toml` — add
|
- **Task 1.1** (cli-engineer): `pyproject.toml` — add
|
||||||
@@ -103,7 +115,11 @@ with `mode` + `selection_reason`. The CLI is installable and every
|
|||||||
|
|
||||||
**Goal:** Dual-use `core/lambda/contract_ingestor.py` (Lambda + CLI
|
**Goal:** Dual-use `core/lambda/contract_ingestor.py` (Lambda + CLI
|
||||||
paths share ≥80% code); `core/env.py:+synthesize_local_env()` for
|
paths share ≥80% code); `core/env.py:+synthesize_local_env()` for
|
||||||
`nova apply --local`; `.nova/contract.yml.attestations/` scaffolded.
|
`nova apply --local`; `.nova/contract.yml.attestations/` scaffolded;
|
||||||
|
JWS-from-PAT key derivation (C-5.2).
|
||||||
|
|
||||||
|
**Exit criterion:** all REQ-329..331 tests pass + JWS-from-PAT KDF
|
||||||
|
specified.
|
||||||
|
|
||||||
#### Wave 1 — dual-use refactor (backend-engineer)
|
#### Wave 1 — dual-use refactor (backend-engineer)
|
||||||
- **Task 1.1** (backend-engineer): refactor
|
- **Task 1.1** (backend-engineer): refactor
|
||||||
@@ -113,7 +129,7 @@ paths share ≥80% code); `core/env.py:+synthesize_local_env()` for
|
|||||||
precedent per RESEARCH §1.2). Verify ≥80% code share (CAP-034 / code
|
precedent per RESEARCH §1.2). Verify ≥80% code share (CAP-034 / code
|
||||||
review). Local path via `core/local_emulators.py:LocalLambdaStub`.
|
review). Local path via `core/local_emulators.py:LocalLambdaStub`.
|
||||||
|
|
||||||
#### Wave 2 — local env synthesizer (backend-engineer)
|
#### Wave 2 — local env synthesizer + JWS KDF (backend-engineer)
|
||||||
- **Task 2.1** (backend-engineer): `core/env.py:+synthesize_local_env()
|
- **Task 2.1** (backend-engineer): `core/env.py:+synthesize_local_env()
|
||||||
` — produces a local env dict (account_id placeholder, region local,
|
` — produces a local env dict (account_id placeholder, region local,
|
||||||
no real AWS) from a contract + `--local` flag. Mirrors
|
no real AWS) from a contract + `--local` flag. Mirrors
|
||||||
@@ -121,6 +137,14 @@ paths share ≥80% code); `core/env.py:+synthesize_local_env()` for
|
|||||||
- **Task 2.2** (cli-engineer): `nova/apply.py` (≤50 lines) — `nova
|
- **Task 2.2** (cli-engineer): `nova/apply.py` (≤50 lines) — `nova
|
||||||
apply --local` delegates to `core.env.synthesize_local_env()` +
|
apply --local` delegates to `core.env.synthesize_local_env()` +
|
||||||
`core.contract_resolver.resolve()`.
|
`core.contract_resolver.resolve()`.
|
||||||
|
- **Task 2.3** (security-engineer): JWS-from-PAT key derivation
|
||||||
|
(C-5.2). HKDF-SHA256(PAT_bytes, salt='nova-local-attestation',
|
||||||
|
info='jws-signing-key') → 32-byte symmetric key. The JWS is
|
||||||
|
HMAC-SHA256 (symmetric, not asymmetric). The "public key derivable
|
||||||
|
from the PAT" AC (REQ-332) is re-interpreted: the *verification key*
|
||||||
|
is derived from the PAT via the same KDF (the PAT is the shared
|
||||||
|
secret). Document in `docs/developer-guide-auth.md`. Update REQ-332
|
||||||
|
AC accordingly.
|
||||||
|
|
||||||
#### Wave 3 — attestations dir (cli-engineer)
|
#### Wave 3 — attestations dir (cli-engineer)
|
||||||
- **Task 3.1** (cli-engineer): verify `nova init` (P1 Wave 2 Task 2.3)
|
- **Task 3.1** (cli-engineer): verify `nova init` (P1 Wave 2 Task 2.3)
|
||||||
@@ -131,18 +155,25 @@ paths share ≥80% code); `core/env.py:+synthesize_local_env()` for
|
|||||||
**Goal:** `nova-idp-auth` Lambda (sign-up, sign-in, session) with
|
**Goal:** `nova-idp-auth` Lambda (sign-up, sign-in, session) with
|
||||||
Argon2id hashing + DynamoDB tables. CAP-036 target.
|
Argon2id hashing + DynamoDB tables. CAP-036 target.
|
||||||
|
|
||||||
|
**Exit criterion:** CAP-036 Verified (E2E sign-up → sign-in → session
|
||||||
|
passes in CI).
|
||||||
|
|
||||||
#### Wave 1 — DynamoDB schema (backend-engineer)
|
#### Wave 1 — DynamoDB schema (backend-engineer)
|
||||||
- **Task 1.1** (backend-engineer): define the 4 DynamoDB table schemas
|
- **Task 1.1** (backend-engineer): define the 4 DynamoDB table schemas
|
||||||
(`nova-users`, `nova-sessions`, `nova-password-resets`, `nova-pats`)
|
(`nova-users`, `nova-sessions`, `nova-password-resets`, `nova-pats`)
|
||||||
in a CloudFormation snippet (reused by P5 `nova idp setup`). PITR
|
in a CloudFormation snippet (reused by P4 Wave 8 `nova idp setup`).
|
||||||
enabled on each (REQ-335).
|
PITR enabled on each (REQ-335).
|
||||||
|
|
||||||
#### Wave 2 — Argon2id (security-engineer)
|
#### Wave 2 — Argon2id (security-engineer) [C-1.2/C-7.2]
|
||||||
- **Task 2.1** (security-engineer): `core/lambda/nova_idp_auth.py` —
|
- **Task 2.1** (security-engineer): `core/lambda/nova_idp_auth.py` —
|
||||||
Argon2id password hashing via `argon2-cffi` (D-228: bundled abi3
|
Argon2id password hashing via `argon2-cffi` (D-228: bundled abi3
|
||||||
wheel; fail-closed on `ImportError`, 503, no pure-Python fallback).
|
wheel; **fail-closed on `ImportError` → 503, no pure-Python
|
||||||
Lambda memory ≥512 MB. Raw passwords never in logs/traces/env/DDB
|
fallback**). **Parameters: t=3, m=65536 KiB, p=1** (OWASP-recommended
|
||||||
(INV-16, REQ-334).
|
minimum). Lambda memory ≥512 MB (m=64 MiB + Python overhead fits).
|
||||||
|
Raw passwords never in logs/traces/env/DDB (INV-16, REQ-334).
|
||||||
|
- **Task 2.2** (security-engineer): `tests/test_argon2_fail_closed.py`
|
||||||
|
— mock `argon2.low_level` import failure → assert auth Lambda
|
||||||
|
returns 503 (not a crash, not a weak hash). C-1.2.
|
||||||
|
|
||||||
#### Wave 3 — auth Lambda (backend-engineer + security-engineer)
|
#### Wave 3 — auth Lambda (backend-engineer + security-engineer)
|
||||||
- **Task 3.1** (backend-engineer): `nova-idp-auth` Lambda handler —
|
- **Task 3.1** (backend-engineer): `nova-idp-auth` Lambda handler —
|
||||||
@@ -157,46 +188,68 @@ Argon2id hashing + DynamoDB tables. CAP-036 target.
|
|||||||
→ sign-in → session round-trip (moto[dynamodb] for local; deployed
|
→ sign-in → session round-trip (moto[dynamodb] for local; deployed
|
||||||
for CI). CAP-036 verification.
|
for CI). CAP-036 verification.
|
||||||
|
|
||||||
### Phase P4 — token-vend-pat (REQ-336..REQ-344)
|
### Phase P4 — token-vend-pat (REQ-336..REQ-344, REQ-340, REQ-341) [was P4+P5]
|
||||||
|
|
||||||
**Goal:** `nova-idp-token-vend` Lambda (KMS-signed OIDC, kyverno-json
|
**Goal:** `nova-idp-token-vend` Lambda (KMS-signed OIDC, kyverno-json
|
||||||
ABAC), JWKS endpoint, PAT lifecycle, `nova auth` commands. CAP-037 +
|
ABAC), JWKS endpoint, PAT lifecycle, `nova auth` commands, **and**
|
||||||
CAP-038 target. **Highest-risk phase** (the `kj` binary in Lambda
|
`nova idp setup` (folded from P5 per C-2.1). CAP-037 + CAP-038 target.
|
||||||
layer — RESEARCH §7).
|
**Highest-risk phase — critical-path.** The kj-binary spike (Wave 1)
|
||||||
|
is the single highest-probability schedule slip; Fargate fallback adds
|
||||||
|
~1 week (D-227). This is a **double-length phase** (8 waves).
|
||||||
|
|
||||||
#### Wave 1 — kj-binary spike (backend-engineer + security-engineer)
|
**Exit criterion:** CAP-037 + CAP-038 Verified + `nova idp setup
|
||||||
|
--check/--apply/--verify` works against a fresh AWS account.
|
||||||
|
|
||||||
|
#### Wave 1 — kj-binary spike (backend-engineer + security-engineer) [C-8.2]
|
||||||
- **Task 1.1** (backend-engineer): confirm the `kj` Go binary
|
- **Task 1.1** (backend-engineer): confirm the `kj` Go binary
|
||||||
(~40 MB Linux amd64) runs in the Lambda Python 3.12 runtime on
|
(~40 MB Linux amd64) runs in the Lambda Python 3.12 runtime on
|
||||||
AL2023. Bundle it in the `nova-cli` layer (`wget` into `layer/bin/kj`,
|
AL2023. Bundle it in the `nova-cli` layer (`wget` a **pinned
|
||||||
`chmod +x`). Verify `KyvernoJsonEngine.is_configured()` finds
|
release** (e.g. `kj@v1.x.y`) + record SHA256 into `layer/kj.sha256`
|
||||||
`/opt/bin/kj`. **If this fails:** fall back to Fargate for the
|
— C-8.2, supply-chain safety) into `layer/bin/kj`, `chmod +x`.
|
||||||
token-vend Lambda (D-227 risk, RESEARCH §7). Escalate to user only
|
Verify `KyvernoJsonEngine.is_configured()` finds `/opt/bin/kj`.
|
||||||
if both fail (full autonomy: log assumption + proceed with Fargate).
|
**If this fails:** fall back to Fargate for the token-vend Lambda
|
||||||
|
(D-227 risk, RESEARCH §7). Escalate to user only if both fail (full
|
||||||
|
autonomy: log assumption + proceed with Fargate).
|
||||||
|
|
||||||
#### Wave 2 — ABAC policy (security-engineer)
|
#### Wave 2 — ABAC policy (security-engineer) [C-5.1]
|
||||||
- **Task 2.1** (security-engineer): `platform/abac/token-vend.policy`
|
- **Task 2.1** (security-engineer): `platform/abac/token-vend.policy`
|
||||||
— kyverno-json `ValidatingPolicy` (D-227). Payload:
|
— kyverno-json `ValidatingPolicy` (D-227). Payload:
|
||||||
`{subject, requested_claims, target_resource, environment, pat_jti,
|
`{subject, requested_claims, target_resource, environment, pat_jti,
|
||||||
policy_version}`. JMESPath checks for role/scope/env/owner. Severity
|
policy_version}`. **`requested_claims` = list of claim names** (the
|
||||||
`critical` = deny on fail.
|
policy asserts the subject is *allowed* to request those claims; the
|
||||||
|
values are assigned by the Lambda, not the requestor — C-5.1).
|
||||||
|
JMESPath checks for role/scope/env/owner. Severity `critical` = deny
|
||||||
|
on fail.
|
||||||
- **Task 2.2** (security-engineer): `policy_version` = git SHA of the
|
- **Task 2.2** (security-engineer): `policy_version` = git SHA of the
|
||||||
policy file, baked into the Lambda layer (D-231). Recorded in every
|
policy file, baked into the Lambda layer (D-231). Recorded in every
|
||||||
`token.vend.allowed/denied` audit event.
|
`token.vend.allowed/denied` audit event.
|
||||||
|
|
||||||
#### Wave 3 — KMS signing (security-engineer)
|
#### Wave 3 — KMS signing (security-engineer) [C-1.1]
|
||||||
- **Task 3.1** (security-engineer): KMS key `alias/nova-oidc-signing`
|
- **Task 3.1** (security-engineer): **verify KMS asymmetric key
|
||||||
|
support** before implementation: `aws kms create-key --key-spec
|
||||||
|
ECC_NIST_P256 --key-usage SIGN_VERIFY` in the target account
|
||||||
|
(C-1.1). If fail: fall back to RSA-2048 (also supported, larger
|
||||||
|
tokens) or escalate. Do not discover this mid-Wave.
|
||||||
|
- **Task 3.2** (security-engineer): KMS key `alias/nova-oidc-signing`
|
||||||
(ECC_NIST_P256, SIGN_VERIFY). Token-vend Lambda signs via
|
(ECC_NIST_P256, SIGN_VERIFY). Token-vend Lambda signs via
|
||||||
`kms.sign(SigningAlgorithm="ECDSA_SHA_256")` → DER→raw ECDSA
|
`kms.sign(SigningAlgorithm="ECDSA_SHA_256")` → DER→raw ECDSA
|
||||||
conversion (`decode_dss_signature` → `r.to_bytes(32) + s.to_bytes(32)`,
|
conversion (`decode_dss_signature` → `r.to_bytes(32) + s.to_bytes(32)`,
|
||||||
RESEARCH §5). JWT header `{"alg":"ES256","typ":"JWT","kid":"..."}`.
|
RESEARCH §5). JWT header `{"alg":"ES256","typ":"JWT","kid":"..."}`.
|
||||||
|
|
||||||
#### Wave 4 — token-vend Lambda (backend-engineer + security-engineer)
|
#### Wave 4 — token-vend Lambda (backend-engineer + security-engineer) [C-6.1/C-7.1 ABAC FAIL-CLOSED]
|
||||||
- **Task 4.1** (backend-engineer): `core/lambda/nova_idp_token_vend.py`
|
- **Task 4.1** (backend-engineer): `core/lambda/nova_idp_token_vend.py`
|
||||||
— accepts PAT/session, validates revocation (`nova-pats.GetItem(jti,
|
— accepts PAT/session, validates revocation (`nova-pats.GetItem(jti,
|
||||||
ConsistentRead=True)` — D-229), evaluates ABAC (Wave 2), signs (Wave
|
ConsistentRead=True)` — D-229), evaluates ABAC (Wave 2), signs (Wave
|
||||||
3), returns OIDC JWT. Audit at every step.
|
3), returns OIDC JWT. Audit at every step.
|
||||||
- **Task 4.2** (security-engineer): token claims `sub, aud, iss, exp,
|
- **Task 4.2** (security-engineer): token claims `sub, aud, iss, exp,
|
||||||
iat, jti, roles` (REQ-336).
|
iat, jti, roles` (REQ-336).
|
||||||
|
- **Task 4.3** (security-engineer) 🔴: **ABAC fail-closed.** If
|
||||||
|
`KyvernoJsonEngine.is_configured()` returns false or `evaluate()`
|
||||||
|
raises, return 403 + audit `token.vend.denied` (reason:
|
||||||
|
`abac_eval_failed`). **Never fail open.** This is INV-17's runtime
|
||||||
|
enforcement (C-6.1/C-7.1 — the grill's #1 finding). Test:
|
||||||
|
`tests/test_abac_fail_closed.py` — mock `kj` absent → assert 403 +
|
||||||
|
audit event.
|
||||||
|
|
||||||
#### Wave 5 — JWKS endpoint (backend-engineer)
|
#### Wave 5 — JWKS endpoint (backend-engineer)
|
||||||
- **Task 5.1** (backend-engineer): `core/lambda/nova_idp_jwks.py` —
|
- **Task 5.1** (backend-engineer): `core/lambda/nova_idp_jwks.py` —
|
||||||
@@ -204,15 +257,20 @@ layer — RESEARCH §7).
|
|||||||
`kms.get_public_key` → DER SPKI → JWK via `cryptography`. Returns
|
`kms.get_public_key` → DER SPKI → JWK via `cryptography`. Returns
|
||||||
`{"keys":[...]}`. Custom domain + WAF = optional (D-230).
|
`{"keys":[...]}`. Custom domain + WAF = optional (D-230).
|
||||||
|
|
||||||
#### Wave 6 — PAT lifecycle (security-engineer + cli-engineer)
|
#### Wave 6 — PAT lifecycle (security-engineer + cli-engineer) [C-7.3]
|
||||||
- **Task 6.1** (security-engineer): PAT issuance — signed JWT
|
- **Task 6.1** (security-engineer): PAT issuance — signed JWT
|
||||||
(`typ: "developer_pat"`, INV-14), `nova-pats` PutItem (jti, pat_hash,
|
(`typ: "developer_pat"`, INV-14), `nova-pats` PutItem (jti, pat_hash,
|
||||||
status=active). Only hash stored (REQ-343). Revoked PATs retained.
|
status=active). Only hash stored (REQ-343). Revoked PATs retained.
|
||||||
|
**Max TTL: ≤24h for developer PATs, ≤1h for service-account PATs**
|
||||||
|
(C-6.2 threat model).
|
||||||
- **Task 6.2** (cli-engineer): `nova/auth/{login,revoke,status}.py` —
|
- **Task 6.2** (cli-engineer): `nova/auth/{login,revoke,status}.py` —
|
||||||
`nova auth login` (session→OIDC token, store in
|
`nova auth login` (session→OIDC token, store in
|
||||||
`~/.nova/credentials.json` 0600), `nova auth revoke --pat <jti>`,
|
`~/.nova/credentials.json` 0600), `nova auth revoke --pat <jti>`,
|
||||||
`nova auth status` (active credential, mode, selection_reason).
|
`nova auth status` (active credential, mode, selection_reason).
|
||||||
All emit audit events (REQ-344).
|
All emit audit events (REQ-344). **C-7.3: `~/.nova/credentials.json`
|
||||||
|
stores the OIDC token + PAT metadata (jti, exp, type) ONLY — NOT the
|
||||||
|
raw PAT.** The raw PAT is entered once at `nova auth login` and not
|
||||||
|
persisted (reduces filesystem-compromise blast radius).
|
||||||
|
|
||||||
#### Wave 7 — CAP-037/038 (security-engineer)
|
#### Wave 7 — CAP-037/038 (security-engineer)
|
||||||
- **Task 7.1** (security-engineer): `tests/test_kms_roundtrip.py`
|
- **Task 7.1** (security-engineer): `tests/test_kms_roundtrip.py`
|
||||||
@@ -220,43 +278,50 @@ layer — RESEARCH §7).
|
|||||||
verify with `pyjwt`. `tests/test_pat_revocation.py` (REQ-351,
|
verify with `pyjwt`. `tests/test_pat_revocation.py` (REQ-351,
|
||||||
CAP-038) — issue → vend → revoke → assert 403 within 60s P95.
|
CAP-038) — issue → vend → revoke → assert 403 within 60s P95.
|
||||||
|
|
||||||
### Phase P5 — idp-setup (REQ-340, REQ-341)
|
#### Wave 8 — nova idp setup (cli-engineer + backend-engineer) [folded from P5 per C-2.1]
|
||||||
|
|
||||||
**Goal:** `nova idp setup` command with `--check/--apply/--verify`
|
**Goal:** `nova idp setup` command with `--check/--apply/--verify`
|
||||||
modes; CloudFormation template generation + review.
|
modes; CloudFormation template generation + review (REQ-340, REQ-341).
|
||||||
|
|
||||||
#### Wave 1 — CloudFormation template (backend-engineer)
|
- **Task 8.1** (backend-engineer): `nova/idp/setup.py` (+ backend
|
||||||
- **Task 1.1** (backend-engineer): `nova/idp/setup.py` (+ backend
|
|
||||||
helper) — generates the Nova-idp CloudFormation template (raw dict →
|
helper) — generates the Nova-idp CloudFormation template (raw dict →
|
||||||
JSON): 2-3 Lambdas, 4 DDB tables, KMS key, function URLs, IAM roles,
|
JSON): 2-3 Lambdas, 4 DDB tables, KMS key, function URLs, IAM roles,
|
||||||
optional CloudFront/WAF/ACM (`--public-jwks-domain` flag).
|
optional CloudFront/WAF/ACM (`--public-jwks-domain` flag).
|
||||||
|
- **Task 8.2** (cli-engineer): `--check` (prerequisites + IAM policy
|
||||||
#### Wave 2 — setup modes (cli-engineer + backend-engineer)
|
|
||||||
- **Task 2.1** (cli-engineer): `--check` (prerequisites + IAM policy
|
|
||||||
delta), `--apply` (generate → `$PAGER` → `y/N` → `cloudformation
|
delta), `--apply` (generate → `$PAGER` → `y/N` → `cloudformation
|
||||||
deploy --capabilities CAPABILITY_IAM`, NFR-10), `--dry-run` (resource
|
deploy --capabilities CAPABILITY_IAM`, NFR-10), `--dry-run` (resource
|
||||||
list only), `--verify` (KMS round-trip, delegates to REQ-350 test).
|
list only), `--verify` (KMS round-trip, delegates to REQ-350 test).
|
||||||
- **Task 2.2** (backend-engineer): IAM policy delta computation —
|
- **Task 8.3** (backend-engineer): IAM policy delta computation —
|
||||||
compares current `nova-spike-runner` grants to required
|
compares current `nova-spike-runner` grants to required
|
||||||
`cloudformation:*` + `codeartifact:*` + `kms:*` + `lambda:*` +
|
`cloudformation:*` + `codeartifact:*` + `kms:*` + `lambda:*` +
|
||||||
`dynamodb:*` + `ssm:*`.
|
`dynamodb:*` + `ssm:*`.
|
||||||
|
|
||||||
### Phase P6 — docs-integration (REQ-345..REQ-351)
|
### Phase P5 — docs-integration (REQ-345..REQ-351)
|
||||||
|
|
||||||
**Goal:** Operator guide, developer guide, threat model; E2E
|
**Goal:** Operator guide, developer guide, threat model; E2E
|
||||||
integration test; property tests; KMS round-trip; PAT revocation SLO.
|
integration test; property tests; KMS round-trip; PAT revocation SLO.
|
||||||
|
|
||||||
#### Wave 1 — docs (lead-developer + security-engineer)
|
**Exit criterion:** all REQ-345..351 tests pass + docs published +
|
||||||
|
threat model reviewed.
|
||||||
|
|
||||||
|
#### Wave 1 — docs (lead-developer + security-engineer) [C-6.2/C-6.3/C-9.2]
|
||||||
- **Task 1.1** (lead-developer): `docs/operator-guide-idp.md` (REQ-345)
|
- **Task 1.1** (lead-developer): `docs/operator-guide-idp.md` (REQ-345)
|
||||||
— `nova idp setup --check/--apply/--verify`, prerequisite IAM policy,
|
— `nova idp setup --check/--apply/--verify`, prerequisite IAM policy,
|
||||||
CloudFormation review flow.
|
CloudFormation review flow. **C-6.3 additions:** KMS key rotation
|
||||||
|
procedure (90 days), Lambda layer update procedure, DDB PITR restore
|
||||||
|
procedure, emergency PAT revocation (DDB-level, not CLI).
|
||||||
- **Task 1.2** (lead-developer): `docs/developer-guide-auth.md`
|
- **Task 1.2** (lead-developer): `docs/developer-guide-auth.md`
|
||||||
(REQ-346) — signup, signin, login, mode resolution, TTY vs piped
|
(REQ-346) — signup, signin, login, mode resolution, TTY vs piped
|
||||||
stdout behavior.
|
stdout behavior, JWS-from-PAT KDF (P2 Wave 2 Task 2.3).
|
||||||
- **Task 1.3** (security-engineer): `docs/threat-model.md` (REQ-347) —
|
- **Task 1.3** (security-engineer): `docs/threat-model.md` (REQ-347) —
|
||||||
Argon2id storage, KMS signing, JWKS exposure, PAT revocation SLO,
|
Argon2id storage, KMS signing, JWKS exposure, PAT revocation SLO,
|
||||||
ABAC token vending, no-AWS-managed-identity (INV-15), DER→raw ECDSA
|
ABAC token vending, no-AWS-managed-identity (INV-15), DER→raw ECDSA
|
||||||
gotcha.
|
gotcha. **C-6.2 additions:** (a) JWKS unauthenticated endpoint DDoS
|
||||||
|
surface + reserved-concurrency mitigation; (b) PAT theft + max TTL
|
||||||
|
(≤24h dev, ≤1h service-account); (c) ABAC fail-closed guarantee
|
||||||
|
(C-6.1). **C-9.2 addition:** INV-18..21 compression audit — verify
|
||||||
|
the spec's attestation invariant semantics are fully captured by
|
||||||
|
INV-15/16/17 + REQ-332.
|
||||||
|
|
||||||
#### Wave 2 — integration tests (backend-engineer + security-engineer)
|
#### Wave 2 — integration tests (backend-engineer + security-engineer)
|
||||||
- **Task 2.1** (backend-engineer): `tests/test_e2e_idp.py` (REQ-348) —
|
- **Task 2.1** (backend-engineer): `tests/test_e2e_idp.py` (REQ-348) —
|
||||||
@@ -267,9 +332,9 @@ integration test; property tests; KMS round-trip; PAT revocation SLO.
|
|||||||
Wave 7 Task 7.1) + REQ-351 (PAT revocation SLO, P4 Wave 7 Task 7.1)
|
Wave 7 Task 7.1) + REQ-351 (PAT revocation SLO, P4 Wave 7 Task 7.1)
|
||||||
pass in CI.
|
pass in CI.
|
||||||
|
|
||||||
### Phase P7 — final-review-ship (Final Phase)
|
### Phase P6 — final-review-ship (Final Phase)
|
||||||
|
|
||||||
**Goal:** Multi-persona code review across P1..P6; project-health
|
**Goal:** Multi-persona code review across P1..P5; project-health
|
||||||
audit; milestone ship to main; CAP-033..038 Verified.
|
audit; milestone ship to main; CAP-033..038 Verified.
|
||||||
|
|
||||||
#### Wave 1 — review (lead-developer)
|
#### Wave 1 — review (lead-developer)
|
||||||
@@ -282,8 +347,8 @@ audit; milestone ship to main; CAP-033..038 Verified.
|
|||||||
issues in this phase.
|
issues in this phase.
|
||||||
|
|
||||||
#### Wave 3 — milestone ship (lead-developer)
|
#### Wave 3 — milestone ship (lead-developer)
|
||||||
- **Task 3.1** (lead-developer): `ciagent-ship` — merge `phase/07` →
|
- **Task 3.1** (lead-developer): `ciagent-ship` — merge `phase/06` →
|
||||||
`milestone/v1.28-cli-identity` → `main`; tag `v1.27.7` (= the v1.28
|
`milestone/v1.28-cli-identity` → `main`; tag `v1.27.6` (= the v1.28
|
||||||
release); Gitea release with full milestone summary; delete all
|
release); Gitea release with full milestone summary; delete all
|
||||||
milestone branches. Update REQUIREMENTS.md (mark REQ-323..353
|
milestone branches. Update REQUIREMENTS.md (mark REQ-323..353
|
||||||
complete), ROADMAP.md (mark v1.28 complete), STATE.md (append
|
complete), ROADMAP.md (mark v1.28 complete), STATE.md (append
|
||||||
@@ -356,7 +421,6 @@ the audit event chain in CI against a deployed Nova-idp.
|
|||||||
| CAP-036 | Nova-idp auth flow works | P3 | E2E test (sign-up → sign-in → session) passes in CI |
|
| CAP-036 | Nova-idp auth flow works | P3 | E2E test (sign-up → sign-in → session) passes in CI |
|
||||||
| CAP-037 | Token-vend signs via KMS | P4 | KMS round-trip test (REQ-350) passes in CI |
|
| CAP-037 | Token-vend signs via KMS | P4 | KMS round-trip test (REQ-350) passes in CI |
|
||||||
| CAP-038 | PAT issuance + revocation | P4 | Issue → vend → revoke → 403 within 60s P95 (REQ-351) passes in CI |
|
| CAP-038 | PAT issuance + revocation | P4 | Issue → vend → revoke → 403 within 60s P95 (REQ-351) passes in CI |
|
||||||
|
|
||||||
**Release gate (§6 of the spec):** CAP-001..CAP-032 remain Verified;
|
**Release gate (§6 of the spec):** CAP-001..CAP-032 remain Verified;
|
||||||
CAP-033..CAP-038 are Verified; all v1.28 release-gate criteria met.
|
CAP-033..CAP-038 are Verified; all v1.28 release-gate criteria met.
|
||||||
|
|
||||||
@@ -398,3 +462,28 @@ CAP-033..CAP-038 are Verified; all v1.28 release-gate criteria met.
|
|||||||
- [x] MVP/UX CHECK: 3 sections present (User-Facing Surface, Happy Path,
|
- [x] MVP/UX CHECK: 3 sections present (User-Facing Surface, Happy Path,
|
||||||
UX Acceptance Criteria).
|
UX Acceptance Criteria).
|
||||||
- [x] Highest-risk item flagged (P4 Wave 1 kj-binary spike).
|
- [x] Highest-risk item flagged (P4 Wave 1 kj-binary spike).
|
||||||
|
- [x] Grill conditions applied (3 critical + 16 tracked; see GRILL.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cost envelope (C-3.1)
|
||||||
|
|
||||||
|
Monthly estimate for the default (no CloudFront) Nova-idp deployment in
|
||||||
|
account `581513795199`:
|
||||||
|
|
||||||
|
| Resource | Quantity | Pricing | Est. monthly |
|
||||||
|
|----------|----------|---------|-------------|
|
||||||
|
| DynamoDB (on-demand) | 4 tables | $1.25/1M write, $0.25/1M read | ~$1 (pilot volume) |
|
||||||
|
| DynamoDB PITR | 4 tables | $0.20/GB-month | ~$1 (small tables) |
|
||||||
|
| KMS asymmetric key | 1 key | $1/key-month + $0.03/10k signs | ~$1 |
|
||||||
|
| Lambda invocations | 3 Lambdas | $0.20/1M req + $0.000016/GB-s | ~$2 (low volume) |
|
||||||
|
| Lambda layer storage | ~50 MB | $0.02/GB-month | <$1 |
|
||||||
|
| CodeArtifact | 1 domain + 1 repo | $1/domain + $1/repo | $2 |
|
||||||
|
| SSM Parameter | 1 | $0.05/param (advanced) | <$1 |
|
||||||
|
| **Total (default)** | | | **~$9/month** |
|
||||||
|
|
||||||
|
Optional CloudFront + WAF + ACM (if `--public-jwks-domain`): +~$3/month
|
||||||
|
at pilot volume. ACM is free for CloudFront-attached certs.
|
||||||
|
|
||||||
|
This is a pilot-scale cost envelope. Production scale (100x volume)
|
||||||
|
would still be <$50/month. No hidden costs identified.
|
||||||
+26
-23
@@ -408,8 +408,11 @@ provisioning cloud resources.
|
|||||||
#### REQ-332 — JWS signing key from PAT
|
#### REQ-332 — JWS signing key from PAT
|
||||||
**Journeys:** J2. **Priority:** High.
|
**Journeys:** J2. **Priority:** High.
|
||||||
**AC:** Given a PAT, when Dev runs `nova apply --local --sign-local-review`,
|
**AC:** Given a PAT, when Dev runs `nova apply --local --sign-local-review`,
|
||||||
then a JWS attestation is produced; the public key is derivable from the
|
then a JWS attestation is produced; the JWS is HMAC-SHA256 with a key
|
||||||
PAT and the JWS verifies. INV-14..17 (attestation invariants) enforced.
|
derived from the PAT via `HKDF-SHA256(PAT_bytes, salt='nova-local-attestation',
|
||||||
|
info='jws-signing-key')` → 32-byte symmetric key (C-5.2 grill fix). The
|
||||||
|
verification key is derived from the PAT via the same KDF (the PAT is
|
||||||
|
the shared secret). INV-14..17 (attestation invariants) enforced.
|
||||||
|
|
||||||
#### REQ-333 — `nova-idp-auth` Lambda
|
#### REQ-333 — `nova-idp-auth` Lambda
|
||||||
**Journeys:** J1, J2. **Priority:** High.
|
**Journeys:** J1, J2. **Priority:** High.
|
||||||
@@ -578,24 +581,24 @@ All v1.28 release-gate criteria in PLAN.md §6 met.
|
|||||||
| REQ-330 | P2 | planned |
|
| REQ-330 | P2 | planned |
|
||||||
| REQ-331 | P2 | planned |
|
| REQ-331 | P2 | planned |
|
||||||
| REQ-332 | P2 | planned |
|
| REQ-332 | P2 | planned |
|
||||||
| REQ-333 | P2 | planned |
|
| REQ-333 | P3 | planned |
|
||||||
| REQ-334 | P2 | planned |
|
| REQ-334 | P3 | planned |
|
||||||
| REQ-335 | P2 | planned |
|
| REQ-335 | P3 | planned |
|
||||||
| REQ-336 | P2 | planned |
|
| REQ-336 | P4 | planned |
|
||||||
| REQ-337 | P2 | planned |
|
| REQ-337 | P4 | planned |
|
||||||
| REQ-338 | P2 | planned |
|
| REQ-338 | P4 | planned |
|
||||||
| REQ-339 | P2 | planned |
|
| REQ-339 | P4 | planned |
|
||||||
| REQ-340 | P2 | planned |
|
| REQ-340 | P4 | planned |
|
||||||
| REQ-341 | P2 | planned |
|
| REQ-341 | P4 | planned |
|
||||||
| REQ-342 | P2 | planned |
|
| REQ-342 | P4 | planned |
|
||||||
| REQ-343 | P2 | planned |
|
| REQ-343 | P4 | planned |
|
||||||
| REQ-344 | P2 | planned |
|
| REQ-344 | P4 | planned |
|
||||||
| REQ-345 | P3 | planned |
|
| REQ-345 | P5 | planned |
|
||||||
| REQ-346 | P3 | planned |
|
| REQ-346 | P5 | planned |
|
||||||
| REQ-347 | P3 | planned |
|
| REQ-347 | P5 | planned |
|
||||||
| REQ-348 | P4 | planned |
|
| REQ-348 | P5 | planned |
|
||||||
| REQ-349 | P4 | planned |
|
| REQ-349 | P5 | planned |
|
||||||
| REQ-350 | P4 | planned |
|
| REQ-350 | P5 | planned |
|
||||||
| REQ-351 | P4 | planned |
|
| REQ-351 | P5 | planned |
|
||||||
| REQ-352 | P5 | planned |
|
| REQ-352 | P6 | planned |
|
||||||
| REQ-353 | P5 | planned |
|
| REQ-353 | P6 | planned |
|
||||||
Reference in New Issue
Block a user