Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions docs/decisions/README.md
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`)
85 changes: 85 additions & 0 deletions docs/decisions/artifact-cas.md
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.

Copy link
Copy Markdown
Contributor

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 Upload failure. Narrow these claims to match the actual branches.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/decisions/artifact-cas.md, line 85:

<comment>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 `Upload` failure. Narrow these claims to match the actual branches.</comment>

<file context>
@@ -81,5 +81,5 @@ None.
-- **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`). 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 (`app/artifact-cas/internal/service/bytestream.go`). Proposed: the owner decides between a code fix and a spec.
+- **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.
</file context>
Suggested change
- **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.
- **Client cache reload trigger.** [issue-3543 D-009](../specs/issue-3543-cas-existence-cache.md) specifies one reload on an authentication error. The code retries an existence check once only when the failed client was cached, regardless of error type, and drops the cached client on backend `Upload` failures without retrying the upload (`app/artifact-cas/internal/service/bytestream.go`). Proposed: the owner decides between a code fix and a spec.

51 changes: 51 additions & 0 deletions docs/decisions/attestation-cli.md
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 .chainloop.yml, per R-007. Say that instead of "at the root of a repository".

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/decisions/attestation-cli.md, line 9:

<comment>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 `.chainloop.yml`, per R-007. Say that instead of "at the root of a repository".</comment>

<file context>
@@ -0,0 +1,30 @@
+
+## Language
+
+- **Repository config file**: `.chainloop.yml` at the root of a repository. When both `.chainloop.yml` and `.chainloop.yaml` exist, `.chainloop.yml` wins.
+- **`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.
</file context>
Suggested change
- **Repository config file**: `.chainloop.yml` at the root of a repository. When both `.chainloop.yml` and `.chainloop.yaml` exist, `.chainloop.yml` wins.
- **Repository config file**: `.chainloop.yml`, read from the current directory up to the git repository root; the nearest file wins. When both `.chainloop.yml` and `.chainloop.yaml` exist in one directory, `.chainloop.yml` wins.

- **`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`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: attestation verify is not in repositoryOrganizationCommands, so it never reads organization from .chainloop.yml. Narrow this line to the five commands that do (init, add, push, status, reset), or mark the rest as drift.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/decisions/attestation-cli.md, line 20:

<comment>`attestation verify` is not in `repositoryOrganizationCommands`, so it never reads `organization` from `.chainloop.yml`. Narrow this line to the five commands that do (`init`, `add`, `push`, `status`, `reset`), or mark the rest as drift.</comment>

<file context>
@@ -17,28 +17,28 @@
 - Rejected: a new `project` key.
 - Source: [issue-3063-attestation-repo-config D-001](../specs/issue-3063-attestation-repo-config.md)
-- Code: not built yet: issue #3063 is open; the code already does it (#3502, #3517, #3518). Attestation commands read `organization`, and `attestation init` reads `projectName` (`app/cli/internal/repositoryconfig/config.go`, `app/cli/cmd/attestation.go`)
+- 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`)
 
 ### Order for the organization
</file context>
Suggested change
- 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`)
- Code: verified (#3502, #3517, #3518): `add`, `init`, `push`, `reset`, and `status` read `organization`, and `attestation init` reads `projectName` (`app/cli/internal/repositoryconfig/config.go`, `app/cli/cmd/attestation.go`); `attestation verify` does not


### 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.
59 changes: 59 additions & 0 deletions docs/decisions/authentication.md
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These entries omit the - Rejected: line that the commit's stated format requires, and the spec's recorded alternatives are dropped. Add a Rejected line to each entry, starting with Token delivery, using the spec's Alternatives considered.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/decisions/authentication.md, line 17:

<comment>These entries omit the `- Rejected:` line that the commit's stated format requires, and the spec's recorded alternatives are dropped. Add a Rejected line to each entry, starting with Token delivery, using the spec's Alternatives considered.</comment>

<file context>
@@ -12,14 +12,41 @@
-| Exchange endpoint | gRPC only, unauthenticated, with no HTTP mapping. Supersedes D-003 | No client needs a plain HTTP route, and the public surface stays smaller. | [issue-3626-login-code-exchange D-013](../specs/issue-3626-login-code-exchange.md) | verified: no `google.api.http` option on the RPC |
+### Token delivery
+A single-use handoff code that the client exchanges for the token.
+- Why: It takes the token out of the URL.
+- Source: [issue-3626-login-code-exchange D-001](../specs/issue-3626-login-code-exchange.md)
+- Code: verified: `ExchangeHandoff` in `internal/service/auth.go`
</file context>

- 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 eng-spec --decisions so the drift and conflict text comes from the tool, or record the code change in a spec as the Open conflicts entry proposes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/decisions/authentication.md, line 59:

<comment>This page is marked as generated and says not to edit it by hand, but these lines were hand-written. Regenerate the page with `eng-spec --decisions` so the drift and conflict text comes from the tool, or record the code change in a spec as the Open conflicts entry proposes.</comment>

<file context>
@@ -56,4 +56,4 @@ Not shown: 6 smaller decisions of [issue-3626-login-code-exchange](../specs/issu
 ## 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`). Proposed: write a spec that records the change. The decision expected "a separate change" for it.
+- **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.
</file context>

Loading
Loading