docs(research): OIDC blocker found; adopt D-039..D-046; re-scope Phase 08
---ci---
project: acdl
phase: 0
milestone: v1.1
status: research
research_targets: 10
top_finding:
- TARGET 1 (conf 0.95): Gitea Actions does NOT support id-token: write / OIDC.
Real OIDC deferred to v1.2 (blocked on go-gitea/gitea#36988).
Spike adopts D-039: per-run-rotated long-lived key waiver.
decisions_adopted: [D-039, D-040, D-041, D-042, D-043, D-044, D-045, D-046]
risks:
- R-1 (highest): OIDC gap -> key-rotation waiver (mitigated by D-039)
- R-2: Checkov terraform_plan edge cases (mitigated by ACDL_TAG_NAMING skipped record)
- R-3: 6 confidence inputs are a recommendation (D-040 adopted in PROJECT.md)
---/ci---
RESEARCH.md (1330 lines) authored by ci-researcher. Key findings:
- TARGET 1: Gitea Actions does NOT support emitting an OIDC id-token
(Gitea docs list id-token as unsupported; go-gitea/gitea#33681 open;
draft PR #36988 unmerged; Gitea's own CI still uses long-lived keys
per #37980). Real-AWS OIDC is unachievable in this environment for v1.1.
- TARGETS 2-10: Terraform/Checkov install steps, IR prior art, Checkov
adapter, DynamoDB outbox, 6-input confidence signal, tiered ledger
(Object Lock deferred), HITL/SoD, contract schema, archive strategy
— all resolved with concrete sketches in RESEARCH.md.
Adopted D-039..D-046 in PROJECT.md. Re-scoped Phase 08 (ROADMAP) and
REQ-23 (REQUIREMENTS) to reflect the OIDC blocker: the spike uses a
per-run-rotated long-lived key (D-039) instead of OIDC; real OIDC is a
v1.2 deliverable. ARCHITECTURE.md Gitea API surface table updated with
the OIDC verdict.
This commit is contained in:
@@ -268,8 +268,8 @@ L3B catalog. Those are post-spike (v1.2+) platform build-out.
|
||||
| Environments API | **None**; act_runner ignores `environment:` | Model HITL gates via `workflow_dispatch` approval inputs (v1.0 D-013 pattern) — **refined in Phase 07** for the real pre-execution gate model |
|
||||
| `repository_dispatch` | Not supported | Cross-repo trigger via `workflow_dispatch` API (unchanged) |
|
||||
| Reusable workflows | Supported | `acdl/.gitea/workflows/pipeline.yml` via `uses: ...@<ref>` |
|
||||
| `id-token: write` / OIDC | **To be confirmed in RESEARCH** (highest-priority unknown) | If act_runner supports OIDC, configure AWS trust; if not, use a documented spike-only waiver and revisit in v1.2 |
|
||||
| `actions/configure-aws-credentials` | Depends on OIDC support | If OIDC unavailable, fall back to short-lived `aws sts assume-role-with-web-identity` from a step |
|
||||
| `id-token: write` / OIDC | **Not supported** (RESEARCH TARGET 1, conf 0.95). Gitea docs list `id-token` as an unsupported GitHub-only scope; open proposal go-gitea/gitea#33681; draft PR go-gitea/gitea#36988 unmerged. Even Gitea's own CI uses long-lived AWS keys (issue #37980). | **Spike waiver D-039:** per-run-rotated long-lived key (rotated after each run by `scripts/rotate_spike_key.sh`). Real OIDC deferred to v1.2, blocked on PR #36988. |
|
||||
| `actions/configure-aws-credentials` | Unusable without OIDC | Spike uses static AWS creds from a (rotated) Gitea Actions secret via the `aws-actions/configure-aws-credentials@v4` `access-key-id`/`secret-access-key` inputs, or plain `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` env vars. v1.2 switches to `role-to-assume` when OIDC lands. |
|
||||
|
||||
### Branch pinning rule (refined for W2.A)
|
||||
|
||||
|
||||
@@ -162,6 +162,14 @@ decisions:
|
||||
| D-036 | Spike picks `l1-s3` + `l2-static-asset` | Simplest real AWS resource (no IAM/network deps); smallest real `terraform plan`; proves the IR + adapter end-to-end | Spike scope fixed |
|
||||
| D-037 | Demo archived to `demo/` (not deleted) | Preserves the working v1.0 demo as intent reference; new platform layout under `platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/` | No churn on demo code; clean separation |
|
||||
| D-038 | Open decisions resolved in "accept recommendations + decide rest" mode | User-locked mode: accept architecture's stated recommendations (W1.A, W1.B, W2.A, BA.A); lead-developer decides the remaining 8 (W3.D, W3.E, BA.B, BA.C, BA.D, BA.E, BA.F, OpenTofu timing) with rationale | Architecture reaches v1.0 in Phase 07 |
|
||||
| D-039 | Spike-only waiver: per-run-rotated long-lived AWS key. OIDC federation deferred to v1.2, blocked on go-gitea/gitea#36988. | **RESEARCH TARGET 1 verdict (conf 0.95):** Gitea Actions does NOT support `id-token: write` / OIDC token issuance as of Gitea 1.27.x / gitea-runner v2.1.0. GitHub's OIDC pattern is not portable. The waiver satisfies §12.5's *intent* (no persistent long-lived key) for the spike: the key is rotated after each run by `scripts/rotate_spike_key.sh`. v1.2 implements real OIDC when the Gitea PR merges. | Spike achieves real `terraform plan` against AWS without a *persistently* long-lived key; real OIDC is a v1.2 deliverable |
|
||||
| D-040 | The 6 confidence-signal inputs are: policy (0.30), validation (0.25), freshness (0.10), source (0.15), history (0.10), nfrs (0.10). Weights frozen for v1, tuned in v1.2 alongside thresholds (BA.B). | Architecture §8 locks "six canonical inputs" but does not enumerate them; RESEARCH TARGET 6 chose the platform-computable subset present in every environment (incl. dev). | Confidence signal (Phase 10) has a concrete input enumeration |
|
||||
| D-041 | Spike audit ledger = v1.0 hash chain + DynamoDB outbox + `acdl-evidence` mirror. S3 Object Lock (compliance mode, 7-yr) + JWS (platform KMS key, quarterly rotation) + daily checkpoints are v1.2 build-out, authored as design in Phase 07. | REQ-20 is "design authored," not "implemented." The spike proves the outbox write path; the regulatory ledger is v1.2. | Spike scope stays bounded; REQ-20 satisfied by the Phase 07 design doc |
|
||||
| D-042 | HITL approver identity in Gitea = `gitea.actor` of the `workflow_dispatch` run that sets `approve_qa=true`/`approve_prod=true`/`approve_dr=true`. Separation-of-duties reads `approver_qa` from the DynamoDB outbox and compares to the prod-dispatch `gitea.actor`. | Gitea has no Environments API (re-confirmed in RESEARCH); `gitea.actor` is the only approval-identity signal. | SoD design (Phase 07) is concrete for the Gitea forge |
|
||||
| D-043 | Tag/naming compliance deferred for the spike: the Checkov adapter emits a single `SKIPPED` PolicyCheckResult (`ruleId: ACDL_TAG_NAMING`, `severity: info`) so the confidence policy input is non-empty. Custom Checkov YAML rule lands in v1.2. | Checkov has no built-in tag-presence check; a custom rule in the spike is scope creep. | Spike's policy input is non-empty without a custom-rule dependency |
|
||||
| D-044 | DynamoDB outbox = `PAY_PER_REQUEST`; PK `contractId`, SK `eventType#eventTs`, TTL `expire_at` = now + 365d. No separate async worker/DLQ in the spike (RTO = workflow re-run); v1.2 outbox worker + DLQ is a Phase 07 design artifact. | On-demand is zero-cost-at-idle for the spike's single dev submission. | Spike outbox is minimal; v1.2 worker design authored in Phase 07 |
|
||||
| D-045 | Runner tooling: `runs-on: ubuntu-latest`; install `terraform` via HashiCorp apt repo (pin `1.9.*`), `checkov` via pip (pin `>=3.2,<4`, `--break-system-packages`). Neither is pre-installed on the default runner image. | RESEARCH TARGET 2; pinning avoids mid-spike version drift. | Phase 09/10 workflows have a concrete setup step |
|
||||
| D-046 | `act_runner` → `gitea-runner` rename: Phase 07 updates docs to use the current name `gitea-runner` (renamed 2026-04 in gitea/runner#850). | RESEARCH TARGET 1 + R-4: naming drift between v1.0 docs and the current runner. | Docs reflect the current binary name |
|
||||
|
||||
### Open-decision resolutions (Phase 07 deliverable — recorded here for traceability)
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
- **REQ-22:** Contract schema (JSON Schema draft 2020-12) is defined under `schemas/contract.schema.json` with per-env mandatory/optional inputs (W3.E) and `profile: agentic` marker for L3B fields.
|
||||
|
||||
### Category: AWS OIDC Bootstrap
|
||||
- **REQ-23:** AWS OIDC trust is configured — an IAM role + trust policy allows the act_runner to assume a role via OIDC with no long-lived credential. The temporary long-lived key used for the one bootstrap `aws iam` call is rotated immediately after (waiver D-034). State backend: S3 state bucket + DynamoDB lock table.
|
||||
- **REQ-23:** AWS auth bootstrap + state backend for the spike: an S3 state bucket + DynamoDB lock/outbox table + an IAM user with a minimal scoped policy (S3 + DynamoDB + plan-only). The temporary long-lived key is used once (waiver D-034) then rotated via `scripts/rotate_spike_key.sh` after each spike run (D-039). **Real OIDC federation is deferred to v1.2** — Gitea Actions does not support `id-token: write` (RESEARCH TARGET 1, conf 0.95), blocked on go-gitea/gitea#36988.
|
||||
|
||||
### Category: v1 Spike — IR, L1, Adapter
|
||||
- **REQ-24:** One real L1 module `l1-s3` exists under `modules-ir/l1/l1-s3/` with an IR-typed interface (typed inputs/outputs/NFRs) registered in the L1 registry.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+8
-7
@@ -99,18 +99,19 @@ milestone COMPLETE: `v1.2.0` (feature milestone, next minor per ship.md).
|
||||
- `docs/architecture.md` status note updated to v1.0 (or a `docs/architecture-v1.0.md` snapshot).
|
||||
|
||||
### Phase 08 — aws-oidc-bootstrap
|
||||
- **Description:** Use the temporary long-lived key (waiver D-034) **once** to create an IAM OIDC provider + IAM role with a trust policy for the act_runner, an S3 state bucket, and a DynamoDB lock table. **Rotate the key immediately after.** Verify the runner assumes the role via OIDC with no long-lived secret.
|
||||
- **Description:** **Re-scoped per RESEARCH TARGET 1 + D-039.** Gitea Actions does not support `id-token: write` (conf 0.95), so real OIDC is deferred to v1.2. This phase instead: uses the temporary long-lived key (waiver D-034) once to create an S3 state bucket, a DynamoDB lock/outbox table, and an IAM user with a minimal scoped policy (S3 + DynamoDB + plan-only); stores the key as a Gitea Actions secret; implements `scripts/rotate_spike_key.sh` to rotate the key after each spike run. Real OIDC federation is tracked via go-gitea/gitea#36988 for v1.2.
|
||||
- **Status:** pending
|
||||
- **Depends on:** [07]
|
||||
- **Requirements:** REQ-23
|
||||
- **Requirements:** REQ-23 (re-interpreted: AWS auth bootstrap + state backend; OIDC deferred to v1.2 per D-039)
|
||||
- **Success Criteria:**
|
||||
- IAM OIDC provider + role exist; trust policy permits the act_runner to assume the role.
|
||||
- S3 state bucket + DynamoDB lock table exist.
|
||||
- The long-lived key is rotated/deactivated (logged in `PROJECT.md` as D-034 closure).
|
||||
- A workflow step assumes the role via OIDC and runs `aws sts get-caller-identity` successfully with no long-lived credential.
|
||||
- S3 state bucket + DynamoDB lock/outbox table exist.
|
||||
- An IAM user with a minimal scoped policy exists; its access key is stored as a Gitea Actions secret.
|
||||
- `scripts/rotate_spike_key.sh` rotates the key (deactivates old, creates new, updates the secret) and is idempotent.
|
||||
- A workflow step authenticates to AWS with the rotated secret and runs `aws sts get-caller-identity` successfully.
|
||||
- D-034 is closed: the bootstrap long-lived key is rotated/deactivated (logged in `PROJECT.md`).
|
||||
|
||||
### Phase 09 — v1-spike-ir-and-l1-and-adapter
|
||||
- **Description:** Implement the Target Stack IR, one real L1 `l1-s3` (IR-typed interface, registered), and the Terraform adapter that compiles the IR → Terraform `variable`/`output` + root module and emits a real `terraform plan` against AWS via OIDC. State in S3 + DynamoDB.
|
||||
- **Description:** Implement the Target Stack IR, one real L1 `l1-s3` (IR-typed interface, registered), and the Terraform adapter that compiles the IR → Terraform `variable`/`output` + root module and emits a real `terraform plan` against AWS (via the rotated-key secret per D-039; OIDC is v1.2). State in S3 + DynamoDB.
|
||||
- **Status:** pending
|
||||
- **Depends on:** [08]
|
||||
- **Requirements:** REQ-24, REQ-26
|
||||
|
||||
Reference in New Issue
Block a user