Repository navigation
decisions: refresh all areas #3727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
88d3eb3
2894b1a
1e9f691
e59aa63
47ccfad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # Current Decisions | ||
|
|
||
| > Generated by `eng-spec --decisions`. To change a decision, write a spec. Do not edit this page by hand. | ||
|
|
||
| Last refresh: 2026-10-23, commit 01d4899e. | ||
|
|
||
| ## Areas | ||
|
|
||
| ### Trace | ||
| - **Page:** [trace.md](trace.md) | ||
| - **Owner:** jiparis | ||
| - **Purpose:** Chainloop Trace: recording AI coding sessions as evidence, from harness hooks to spec capture, line attribution, and local storage. | ||
| - **Does not own:** the `.chainloop.yml` reader that attestation commands also use (Attestation CLI), and the CAS that stores the materials (Artifact CAS). | ||
| - **Specs:** [issue-3495-session-spec-capture](../specs/issue-3495-session-spec-capture.md), [issue-3515-spec-capture-during-session](../specs/issue-3515-spec-capture-during-session.md), [issue-3520-pi-trace-provider](../specs/issue-3520-pi-trace-provider.md), [issue-3531-spec-source-role](../specs/issue-3531-spec-source-role.md), [issue-3540-trace-skills-used](../specs/issue-3540-trace-skills-used.md), [issue-3556-session-image-placeholders](../specs/issue-3556-session-image-placeholders.md), [issue-3561-up-to-date-local-spec-evidence](../specs/issue-3561-up-to-date-local-spec-evidence.md), [issue-3569-pasted-image-pointers](../specs/issue-3569-pasted-image-pointers.md), [issue-3582-trace-evidence-export](../specs/issue-3582-trace-evidence-export.md), [issue-3635-trace-e2e-matrix](../specs/issue-3635-trace-e2e-matrix.md), [issue-3639-trace-line-attribution](../specs/issue-3639-trace-line-attribution.md), [issue-3639-line-ranges](../specs/issue-3639-line-ranges.md), [issue-3657-codex-trace-provider](../specs/issue-3657-codex-trace-provider.md) | ||
| - **Code:** `app/cli/internal/trace/`, `app/cli/pkg/action/trace_*.go`, `app/cli/cmd/trace*.go`, `pkg/attestation/crafter/materials/aicodingsession/` | ||
|
|
||
| ### Attestation CLI | ||
| - **Page:** [attestation-cli.md](attestation-cli.md) | ||
| - **Owner:** migmartri | ||
| - **Purpose:** The `chainloop attestation` commands and the repository settings they read. | ||
| - **Does not own:** the Trace keys of `.chainloop.yml` (`traceStorage`, `tracePath`, `traceRequired`), which Trace owns. | ||
| - **Specs:** [issue-3063-attestation-repo-config](../specs/issue-3063-attestation-repo-config.md) | ||
| - **Code:** `app/cli/cmd/attestation_*.go`, `app/cli/internal/repositoryconfig/` | ||
|
|
||
| ### Artifact CAS | ||
| - **Page:** [artifact-cas.md](artifact-cas.md) | ||
| - **Owner:** migmartri | ||
| - **Purpose:** Upload and download of artifacts through the CAS proxy, and its caches. | ||
| - **Does not own:** the ports, ingress, and protocol dispatch of the CAS service (Service endpoints). | ||
| - **Specs:** [issue-3543-cas-existence-cache](../specs/issue-3543-cas-existence-cache.md), [issue-3549-cas-download-browser-cache](../specs/issue-3549-cas-download-browser-cache.md) | ||
| - **Code:** `app/artifact-cas/`, `pkg/cache/`, `pkg/blobmanager/` | ||
|
|
||
| ### Service endpoints | ||
| - **Page:** [service-endpoints.md](service-endpoints.md) | ||
| - **Owner:** migmartri | ||
| - **Purpose:** How the control plane and the Artifact CAS serve gRPC, gRPC-Web, and HTTP, and how the Helm chart exposes them. | ||
| - **Does not own:** what each API does (the area of that API). | ||
| - **Specs:** [issue-3628-single-endpoint](../specs/issue-3628-single-endpoint.md), [issue-3628-controlplane-http-dispatch](../specs/issue-3628-controlplane-http-dispatch.md) | ||
| - **Code:** `app/controlplane/internal/server/`, `app/artifact-cas/internal/server/`, `deployment/chainloop/` | ||
|
|
||
| ### Authentication | ||
| - **Page:** [authentication.md](authentication.md) | ||
| - **Owner:** migmartri | ||
| - **Purpose:** User login through OIDC, and how the session token reaches the CLI and the web UI. | ||
| - **Does not own:** API tokens and authorization policies. | ||
| - **Specs:** [issue-3626-login-code-exchange](../specs/issue-3626-login-code-exchange.md) | ||
| - **Code:** `app/controlplane/internal/service/auth.go`, `app/controlplane/pkg/biz/loginhandoff.go`, `app/cli/cmd/auth_login.go` | ||
|
|
||
| ## Relationships | ||
|
|
||
| - **Trace → Attestation CLI:** both read `.chainloop.yml` through one reader. A change to the file format or the reader order changes both. | ||
| - **Trace → Artifact CAS:** each spec source, skill, and pasted image is a material in the CAS. A transcript pointer is replaced only when its material is in the CAS. Platform viewers show these materials through the CAS download cache. | ||
| - **Authentication → Artifact CAS:** the browser download login uses the login flow of Authentication. | ||
| - **Service endpoints → Artifact CAS, Authentication:** the CAS and the login endpoints are served on the ports that Service endpoints defines. | ||
|
|
||
| ## Repository-wide decisions | ||
|
|
||
| ### File readers | ||
| One reader of `.chainloop.yml` for attestation and trace. | ||
| - Why: Two readers already disagree on file order. | ||
| - Rejected: a second change to the attestation reader only. | ||
| - Source: [issue-3063-attestation-repo-config D-007](../specs/issue-3063-attestation-repo-config.md) | ||
| - Code: verified (#3502, #3518): `repositoryconfig.LoadChainloopYML` is the only reader of settings, and Trace calls it too. The Trace writer that updates keys parses the file itself, to keep its formatting (`app/cli/internal/trace/config/config.go`) (`app/cli/internal/repositoryconfig/config.go`) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # Artifact CAS: Current Decisions | ||
|
|
||
| > Generated by `eng-spec --decisions`. To change a decision, write a spec. Do not edit this page by hand. | ||
|
|
||
| **Purpose:** Upload and download of artifacts through the CAS proxy, and its caches. **Owner:** migmartri. **Does not own:** the ports, ingress, and protocol dispatch of the CAS service (Service endpoints). **Specs:** [issue-3543-cas-existence-cache](../specs/issue-3543-cas-existence-cache.md), [issue-3549-cas-download-browser-cache](../specs/issue-3549-cas-download-browser-cache.md). **Last refresh:** 2026-10-23, commit 01d4899e. | ||
|
|
||
| ## Language | ||
|
|
||
| - **Existence cache**: the server-side cache of positive "this blob exists" results, per organization and backend. It never holds a "not found". | ||
| - **Client cache**: the in-memory, short-lived cache of loaded storage clients. Credentials never leave the process. | ||
| - **Download token**: the token that allows one download. A browser sends it in the `Authorization: Bearer` header, so the download URL stays the same for a digest. _Avoid_: the `?t=` query parameter for browser downloads, because a changing URL defeats the browser cache. | ||
|
|
||
| ## Current decisions | ||
|
|
||
| ### Cache store | ||
| Shared cache library: NATS KV when connected, in-memory LRU otherwise. | ||
| - Why: Shared hits across replicas, with no new hard dependency. | ||
| - Rejected: memory only, and NATS required. | ||
| - Source: [issue-3543-cas-existence-cache D-001](../specs/issue-3543-cas-existence-cache.md) | ||
| - Code: verified: `pkg/cache/cache.go`, `pkg/cache/casexistence/` | ||
|
|
||
| ### Where the cache lives | ||
| Artifact CAS, server side. | ||
| - Why: Works for all CLI versions with no API change. | ||
| - Rejected: a cache in the CLI. | ||
| - Source: [issue-3543-cas-existence-cache D-003](../specs/issue-3543-cas-existence-cache.md) | ||
| - Code: verified: `ByteStreamService.Write` | ||
|
|
||
| ### Cache key and tenant namespace | ||
| Organization first, then backend type, hashed secret reference, digest, in a separate bucket. | ||
| - Why: One tenant's upload must never answer for another tenant. | ||
| - Rejected: the digest alone. | ||
| - Source: [issue-3543-cas-existence-cache D-004](../specs/issue-3543-cas-existence-cache.md) | ||
| - Code: verified: bucket `chainloop-cas-existence` | ||
|
|
||
| ### Negative results | ||
| Not cached. | ||
| - Why: A missing blob can appear at any time, and the gain is small. | ||
| - Source: [issue-3543-cas-existence-cache D-005](../specs/issue-3543-cas-existence-cache.md) | ||
| - Code: verified: only present blobs are stored | ||
|
|
||
| ### Credentials in a shared store | ||
| Never. The client cache stays in memory. | ||
| - Why: Credentials must not leave the process. | ||
| - Source: [issue-3543-cas-existence-cache D-006](../specs/issue-3543-cas-existence-cache.md) | ||
| - Code: verified: process-local LRU, only the size goes to the shared store | ||
|
|
||
| ### Existence entry lifetime | ||
| 24 hours, configurable. | ||
| - Why: Blobs cannot be deleted through Chainloop, so the only risk is a purge outside it. | ||
| - Rejected: 7 days. | ||
| - Source: [issue-3543-cas-existence-cache D-007](../specs/issue-3543-cas-existence-cache.md) | ||
| - Code: drift: 24 hours is a constant with no configuration, and the cache has no off switch (`pkg/cache/casexistence/casexistence.go`, `app/artifact-cas/cmd/wire.go`). See Open conflicts | ||
|
|
||
| ### Where downloads are cached | ||
| The browser HTTP cache. | ||
| - Why: The browser downloads straight from the CAS. | ||
| - Rejected: a server cache in the control plane. | ||
| - Source: [issue-3549-cas-download-browser-cache D-001](../specs/issue-3549-cas-download-browser-cache.md) | ||
| - Code: verified: `ETag` and 304 path in `internal/service/download.go` | ||
|
|
||
| ### How the browser checks its copy | ||
| `ETag` set to the digest, with `Cache-Control: private, no-cache`. | ||
| - Why: The CAS checks access on each open. | ||
| - Rejected: `max-age` with no check. | ||
| - Source: [issue-3549-cas-download-browser-cache D-002](../specs/issue-3549-cas-download-browser-cache.md) | ||
| - Code: verified: set on the 200 and the 304 | ||
|
|
||
| ### How the URL stays the same | ||
| Token in an `Authorization` header, which wins over the query. | ||
| - Why: The browser cache uses the URL as its key. | ||
| - Rejected: reuse one token per user and digest. | ||
| - Source: [issue-3549-cas-download-browser-cache D-003](../specs/issue-3549-cas-download-browser-cache.md) | ||
| - Code: verified: `pkg/middlewares/http/jwt.go` | ||
|
|
||
| Not shown: 3 smaller decisions of [issue-3543-cas-existence-cache](../specs/issue-3543-cas-existence-cache.md), 3 of [issue-3549-cas-download-browser-cache](../specs/issue-3549-cas-download-browser-cache.md). | ||
|
|
||
| ## Superseded | ||
|
|
||
| None. | ||
|
|
||
| ## Open conflicts | ||
|
|
||
| - **Existence entry lifetime is not configurable.** [issue-3543 D-007](../specs/issue-3543-cas-existence-cache.md) says 24 hours, configurable. The code has a 24-hour constant and no setting (`pkg/cache/casexistence/casexistence.go`). The off switch that R-004 asks for is also missing: the CAS always turns the cache on (`app/artifact-cas/cmd/wire.go`). Proposed: fix the code. | ||
| - **Client cache reload trigger.** [issue-3543 D-009](../specs/issue-3543-cas-existence-cache.md) reloads a client once on an authentication error. The code reloads once on any existence-check error, and drops the cached client on any upload error, with no retry (`app/artifact-cas/internal/service/bytestream.go`). Proposed: the owner decides between a code fix and a spec. | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,51 @@ | ||||||
| # Attestation CLI: Current Decisions | ||||||
|
|
||||||
| > Generated by `eng-spec --decisions`. To change a decision, write a spec. Do not edit this page by hand. | ||||||
|
|
||||||
| **Purpose:** The `chainloop attestation` commands and the repository settings they read. **Owner:** migmartri. **Does not own:** the Trace keys of `.chainloop.yml` (`traceStorage`, `tracePath`, `traceRequired`), which Trace owns. **Specs:** [issue-3063-attestation-repo-config](../specs/issue-3063-attestation-repo-config.md). **Last refresh:** 2026-10-23, commit 01d4899e. | ||||||
|
|
||||||
| ## Language | ||||||
|
|
||||||
| - **Repository config file**: `.chainloop.yml` at the root of a repository. When both `.chainloop.yml` and `.chainloop.yaml` exist, `.chainloop.yml` wins. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The file is not only read from the repository root. The reader walks up from the current directory to the git root and uses the nearest Prompt for AI agents
Suggested change
|
||||||
| - **`projectName`** and **`organization`**: the keys that pin the project and the organization of a repository. _Avoid_: a `project` key, because `chainloop trace init` already writes `projectName`. | ||||||
| - **Saved default**: the organization that the CLI configuration stores. It has the lowest precedence. | ||||||
|
|
||||||
| ## Current decisions | ||||||
|
|
||||||
| ### Key names in the file | ||||||
| `projectName` and `organization`. | ||||||
| - Why: `chainloop trace init` already writes these keys. | ||||||
| - Rejected: a new `project` key. | ||||||
| - Source: [issue-3063-attestation-repo-config D-001](../specs/issue-3063-attestation-repo-config.md) | ||||||
| - Code: verified (#3502, #3517, #3518): attestation commands read `organization`, and `attestation init` reads `projectName` (`app/cli/internal/repositoryconfig/config.go`, `app/cli/cmd/attestation.go`) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents
Suggested change
|
||||||
|
|
||||||
| ### Order for the organization | ||||||
| Flag, environment variable, file, saved default. | ||||||
| - Why: The file pins the organization for the repository. | ||||||
| - Rejected: saved default before the file. | ||||||
| - Source: [issue-3063-attestation-repo-config D-002](../specs/issue-3063-attestation-repo-config.md) | ||||||
| - Code: verified (#3518): commands other than init also read the organization from the local attestation state before the saved default (`app/cli/cmd/attestation.go`) | ||||||
|
|
||||||
| ### No project from the flag or the file | ||||||
| Stop with an error. | ||||||
| - Why: The project is a required flag today. | ||||||
| - Rejected: a warning only. | ||||||
| - Source: [issue-3063-attestation-repo-config D-003](../specs/issue-3063-attestation-repo-config.md) | ||||||
| - Code: verified (#3517): `attestation init` stops with "project is required, set it via --project or projectName in .chainloop.yml" | ||||||
|
|
||||||
| ### API token for a different organization | ||||||
| Stop early with a clear error. | ||||||
| - Why: The trace commands do the same check. | ||||||
| - Rejected: send the request and let the control plane fail. | ||||||
| - Source: [issue-3063-attestation-repo-config D-006](../specs/issue-3063-attestation-repo-config.md) | ||||||
| - Code: verified (#3518): the check runs before the connection, and the error names both organizations and the file (`app/cli/cmd/attestation.go`) | ||||||
|
|
||||||
| Not shown: 5 smaller decisions of [issue-3063-attestation-repo-config](../specs/issue-3063-attestation-repo-config.md). The file reader decision (D-007) is in the repository-wide part of the [index](README.md). | ||||||
|
|
||||||
| ## Superseded | ||||||
|
|
||||||
| None. | ||||||
|
|
||||||
| ## Open conflicts | ||||||
|
|
||||||
| - **Issue #3063 is open, but its decisions are in the code.** All checked decisions (D-001, D-002, D-003, D-005, D-006, D-007, D-009) are built (#3502, #3517, #3518, #3593). One addition: commands other than init read the organization from the local attestation state before the saved default, a step that [D-002](../specs/issue-3063-attestation-repo-config.md) does not name. Proposed: run `eng-spec --done` for issue-3063, record the extra step, and close the issue. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # Authentication: Current Decisions | ||
|
|
||
| > Generated by `eng-spec --decisions`. To change a decision, write a spec. Do not edit this page by hand. | ||
|
|
||
| **Purpose:** User login through OIDC, and how the session token reaches the CLI and the web UI. **Owner:** migmartri. **Does not own:** API tokens and authorization policies. **Specs:** [issue-3626-login-code-exchange](../specs/issue-3626-login-code-exchange.md). **Last refresh:** 2026-10-23, commit 01d4899e. | ||
|
|
||
| ## Language | ||
|
|
||
| - **Handoff code**: the short-lived, single-use code that the callback carries in place of the token. The client exchanges it for the token. The callback parameter is `handoff`. _Avoid_: `code` as the parameter name, because a client callback can be an OAuth client that already gets `code` from its own identity provider. | ||
| - **Legacy callback**: the callback that carries the token in the URL (`?t=`). It is deprecated in three phases. | ||
| - **Challenge**: the PKCE-style S256 value that binds a handoff code to the client that started the login. | ||
|
|
||
| ## Current decisions | ||
|
|
||
| ### Token delivery | ||
| A single-use handoff code that the client exchanges for the token. | ||
| - Why: It takes the token out of the URL. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: These entries omit the Prompt for AI agents |
||
| - Source: [issue-3626-login-code-exchange D-001](../specs/issue-3626-login-code-exchange.md) | ||
| - Code: verified: `ExchangeHandoff` in `internal/service/auth.go` | ||
|
|
||
| ### Code binding | ||
| PKCE-style S256 challenge from the client, plus the callback URL. | ||
| - Why: Works for the CLI and a browser app, with no cookie at exchange. | ||
| - Source: [issue-3626-login-code-exchange D-002](../specs/issue-3626-login-code-exchange.md) | ||
| - Code: verified: S256 only, verifier and callback checked | ||
|
|
||
| ### Code store | ||
| Database table with the code hash, the user, the expiry, and an atomic consume. No token at rest. | ||
| - Why: Works with many replicas and enforces single use. | ||
| - Source: [issue-3626-login-code-exchange D-004](../specs/issue-3626-login-code-exchange.md) | ||
| - Code: verified: `ent/schema/loginhandoff.go` | ||
|
|
||
| ### Deprecation | ||
| Three phases: allowed by default, then denied by default, then deleted. | ||
| - Why: Operators get time and a measured deletion condition. | ||
| - Source: [issue-3626-login-code-exchange D-006](../specs/issue-3626-login-code-exchange.md) | ||
| - Code: verified: phase 1, with the `allow_legacy_login_callback` setting | ||
|
|
||
| ### Browser artifact download flow | ||
| No change: it stays on the legacy callback. | ||
| - Why: Its token expires after 10 seconds, and a separate change can move it. | ||
| - Source: [issue-3626-login-code-exchange D-008](../specs/issue-3626-login-code-exchange.md) | ||
| - Code: drift: the download flow now requires a handoff code and rejects `?t=`, whatever the legacy switch says. It always gets a short-lived token (`internal/service/casredirect.go`, `internal/service/auth.go`). See Open conflicts | ||
|
|
||
| ### Exchange endpoint | ||
| gRPC only, unauthenticated, with no HTTP mapping. Supersedes D-003. | ||
| - Why: No client needs a plain HTTP route, and the public surface stays smaller. | ||
| - Source: [issue-3626-login-code-exchange D-013](../specs/issue-3626-login-code-exchange.md) | ||
| - Code: verified: no `google.api.http` option on the RPC | ||
|
|
||
| Not shown: 6 smaller decisions of [issue-3626-login-code-exchange](../specs/issue-3626-login-code-exchange.md). | ||
|
|
||
| ## Superseded | ||
|
|
||
| - [issue-3626-login-code-exchange D-003](../specs/issue-3626-login-code-exchange.md) (exchange endpoint with an HTTP POST mapping), replaced by [issue-3626-login-code-exchange D-013](../specs/issue-3626-login-code-exchange.md). | ||
|
|
||
| ## Open conflicts | ||
|
|
||
| - **Browser download login.** [issue-3626 D-008](../specs/issue-3626-login-code-exchange.md) keeps the download flow on the legacy callback. The code moved it to the handoff code exchange (`app/controlplane/internal/service/casredirect.go`). The legacy switch does not bring it back, and the download token stays short-lived. Proposed: write a spec that records the change. The decision expected "a separate change" for it. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: This page is marked as generated and says not to edit it by hand, but these lines were hand-written. Regenerate the page with Prompt for AI agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The code does not retry every existence-check error or drop the client for every upload-path error: it retries only a failed cached client, and drops the client only on a backend
Uploadfailure. Narrow these claims to match the actual branches.Prompt for AI agents