# Enable universal cookie delivery

> The one-login-password Safe Storage open, the `teamid:` partition list, duplicate-Keychain-item convergence, and unsigned-CGO boundary so any unmodified cookie tool reads the synced Default profile.

- Repository: mvanhorn/agentcookie
- GitHub: https://github.com/mvanhorn/agentcookie
- Human docs: https://grok-wiki.com/public/docs/mvanhorn-agentcookie-137da38edfae
- Complete Markdown: https://grok-wiki.com/public/docs/mvanhorn-agentcookie-137da38edfae/llms-full.txt

## Source Files

- `docs/runbook-v0.13-one-password-keychain.md`
- `docs/runbook-v0.10-keychain-access.md`
- `internal/chrome/keychain.go`
- `internal/chrome/keychain_keybase.go`
- `internal/chrome/launchagent_helper.go`
- `internal/cli/wizard_keychain.go`

---

---
title: "Enable universal cookie delivery"
description: "The one-login-password Safe Storage open, the `teamid:` partition list, duplicate-Keychain-item convergence, and unsigned-CGO boundary so any unmodified cookie tool reads the synced Default profile."
---

Universal cookie delivery on a sink means the real Chrome `Default` profile is written and the Chrome Safe Storage key is readable by unmodified cookie tools (yt-dlp, gallery-dl, pycookiecheat, browser_cookie3, the agentcookie daemon itself). The v0.13 onboarding path opens that read access over SSH with **one** macOS login-password entry, no GUI SecurityAgent prompt, and no destructive rewrite of the keychain item — driven by `agentcookie wizard set-keychain-access` and its inline partition strategy in `internal/cli/wizard_keychain.go` and `internal/chrome/keychain.go`.

## Outcome and verification

A successful open lands the sink in **universal** delivery. `agentcookie doctor` reports the cookie-delivery check as `OK`:

```text
[ok ] Cookie delivery: universal (delivery=universal): real Default profile written and
       Chrome Safe Storage readable; any unmodified cookie CLI works here
```

Independent shell verification:

```bash
agentcookie doctor                                                          # expect: Cookie delivery: universal
security find-generic-password -s "Chrome Safe Storage" -a Chrome -w        # readable via the apple-tool: partition
```

A direct `security` read from a fresh bare-SSH session can still return `User interaction is not allowed` if the login keychain has re-locked; that is a session-lock artifact, not a partition failure. The sink daemon runs in the GUI session where the login keychain is unlocked, so its CGO read via `SecItemCopyMatching` succeeds once the partition is set.

## The one-password Safe Storage open

Earlier versions deleted and recreated the Chrome Safe Storage item from a one-shot LaunchAgent (`-A` or per-binary `-T`), which on modern macOS triggers GUI prompts a headless sink cannot answer. v0.13 replaces that with a single non-destructive `security` call:

```bash
security set-generic-password-partition-list \
  -S "apple-tool:,apple:,teamid:<TEAM>" \
  -k "<login-password>" \
  -s "Chrome Safe Storage" -a Chrome
```

The argv shape is built by `buildPartitionListArgv` and dispatched by `SetSafeStoragePartitionListWithPassword` in `internal/chrome/keychain.go`. The `-k` flag both authorizes the ACL change (`SecKeychainItemSetAccessWithPassword` requires the login password — there is no headless bypass) and unlocks the login keychain for the single call, which is what makes the partition update succeed over SSH where the keychain is otherwise locked (`-25308 User interaction is not allowed`).

Crucial properties of this path:

<ParamField body="No delete, no rewrite" type="invariant">
The Safe Storage **value** is structurally untouched; only the item's access partition list changes. Existing Chrome cookies stay decryptable because Chromium derives its AES-128 key from this value via PBKDF2 (`saltysalt`, 1003 iterations) in `DeriveAESKey`.
</ParamField>

<ParamField body="No LaunchAgent dispatch" type="invariant">
`runInlinePartitionAccess` runs the call inline in the current process. No GUI session is required and no SecurityAgent prompt fires.
</ParamField>

<ParamField body="Password is ephemeral" type="invariant">
The login password is passed as a single discrete argv element to `security` and never logged, persisted, or echoed. It is briefly visible in `ps` for the lifetime of the call — unavoidable for `security -k`.
</ParamField>

<Warning>
Zero password entries is **not** achievable on macOS. One terminal entry (no GUI dialog) is the floor; `-k` is the only way to make the call succeed without a Keychain Access GUI click.
</Warning>

## The `teamid:` partition list

The wizard composes the partition list from the running agentcookie binary's own code signature. `TeamPartitionList(teamID)` in `internal/chrome/keychain.go` returns:

| Entry | Covers | Read path |
| --- | --- | --- |
| `apple-tool:` | `/usr/bin/security` and the popular unmodified cookie tools | `find-generic-password` via the Security CLI |
| `apple:` | Apple-signed system binaries | `SecItemCopyMatching` from Apple binaries |
| `teamid:<TEAM>` | Developer-ID-signed binaries from `<TEAM>` | `SecItemCopyMatching` (CGO/`go-keychain`) |

`<TEAM>` is resolved at runtime from `codesign -d --verbose=2 <self>` by `BinaryTeamID`, which parses the `TeamIdentifier=` line. An ad-hoc or unsigned agentcookie binary yields `("", nil)` and the wizard falls back to `DefaultPartitionList` (`apple-tool:,apple:`) with a stderr warning:

```text
agentcookie wizard: WARNING this agentcookie binary is not Developer-ID-signed; the
partition covers security-CLI cookie tools (apple-tool) but not Dev-ID CGO readers.
The sink daemon needs a signed binary to read via teamid.
```

The Apple-tool entry covers the most common cookie CLIs because they shell out to `security`:

| Unmodified cookie tool | Read path used | Partition entry that grants it |
| --- | --- | --- |
| `yt-dlp` | `security find-generic-password` | `apple-tool:` |
| `gallery-dl` | `security find-generic-password` | `apple-tool:` |
| `browser_cookie3` | `security find-generic-password` | `apple-tool:` |
| `pycookiecheat` | Python `keyring` → `SecItemCopyMatching` (unsigned CGO) | **none** (see boundary) |
| `agentcookie` daemon | `SecItemCopyMatching` via `keybase/go-keychain` | `teamid:<TEAM>` (Developer-ID-signed) |

## The unsigned-CGO boundary

`teamid:` only covers binaries signed with the matching Developer ID team. `apple-tool:` only covers callers that go through `/usr/bin/security`. A binary that is **both** unsigned (or signed with a different team) **and** calls `SecItemCopyMatching` directly is the documented uncovered class: pycookiecheat (Python `keyring` from an unsigned Homebrew python), a locally-compiled `kooky`/`rookie`, any ad-hoc-built Go CGO reader. Its `-25308` over SSH on the daemon-side read is expected, not a bug.

Three options to cover the long tail, in order of preference:

<Steps>
<Step title="Sign the tool with your team">
Re-sign the binary with the same Developer ID team `<TEAM>` so the existing `teamid:` entry covers it. No partition change needed.
</Step>
<Step title="Add the tool to a per-binary trust list">
The legacy `-T <path>` trust-list strategies in `buildStrategies` still apply individual binaries via `--extra-binary <abs path>` to `agentcookie wizard set-keychain-access --recreate`.
</Step>
<Step title="Open to any application (sink-only)">
`agentcookie wizard set-keychain-access --any-app` opts into the legacy delete-and-recreate chain that adds `-A` (any application). It preserves the existing key value via the read-then-reuse guard in `delete-and-recreate-with-A`, but any local process on the box can then read Chrome cookies — only appropriate on a dedicated sink.
</Step>
</Steps>

<Note>
The signed daemon path does **not** require an Always-Allow GUI click. The live macOS 15.3.1 verification confirmed the partition (`apple-tool:,apple:,teamid:NM8VT393AR`) is sufficient for the Developer-ID-signed agentcookie daemon to read Safe Storage in its GUI session.
</Note>

## Duplicate Keychain item convergence

The install-time race that previously stalled sinks at `delivery: degraded`: a CDP-injecting degraded sink relaunches Chrome; Chrome recreates **its own** Chrome Safe Storage item; the keychain now holds more than one item; the partition is set on one while a reader hits another; the verification read fails.

`CountSafeStorageItems` in `internal/chrome/keychain.go` detects this from a locked SSH session — `security dump-keychain` returns metadata only (no secret values), so it succeeds without unlocking. Items are counted by the `"svce"<blob>="Chrome Safe Storage"` marker line.

`convergeSafeStorageToOneItem` (in `internal/cli/wizard_keychain.go`) collapses duplicates **before** the partition set:

```text
flowchart TD
  count[CountSafeStorageItems] --> dup{n > 1?}
  dup -- no --> noop[No-op return 0]
  dup -- yes --> unlock[unlock-keychain -p pw]
  unlock --> read[SafeStoragePassword<br/>read existing value]
  read -- fail --> refuse[Refuse-to-delete guard:<br/>return error, change nothing]
  read -- ok --> del[delete-generic-password x n]
  del --> readd[add-generic-password -w existing<br/>same value, never random]
  readd --> done[return n-1]
```

The cookie-safety invariant (KTD3/KTD4): the existing value is read **first** and the function refuses to delete anything if that read fails, because recreating the item with a different value would permanently destroy all existing Chrome cookies (PBKDF2-derived AES key changes). The surviving item is always re-added with the **same** value, never a fresh random one. The same read-then-reuse guard appears in the `--any-app` `delete-and-recreate-with-A` strategy.

Doctor surfaces the race directly via `checkCookieDelivery` (`internal/cli/doctor.go`):

```text
[WARN] Cookie delivery: race: N Chrome Safe Storage keychain items exist;
       the install-time Chrome-relaunch race left duplicates ...
       Remediation: converge to one item and re-grant: `agentcookie wizard set-keychain-access`
       (quiesces Chrome, collapses duplicates value-preserved, re-sets the partition)
```

To converge manually when something is actively recreating the item, quiesce first:

```bash
launchctl bootout gui/$(id -u)/dev.agentcookie.sink   # stop the CDP injector
pkill -x "Google Chrome"                              # stop the racer
agentcookie wizard set-keychain-access                # converge + grant
```

## Acquiring the login password

`acquireLoginPassword` in `internal/cli/login_password.go` resolves the password in this order:

<ParamField body="AGENTCOOKIE_LOGIN_PASSWORD" type="env" required={false}>
Read straight from the environment if set. Used by fully non-interactive installs (CI, no PTY). Never logged or persisted; briefly visible in `ps` for the `security -k` call.
</ParamField>

<ParamField body="No-echo terminal prompt" type="fallback">
Falls back to a single `bufio` read with terminal echo disabled via `/bin/stty -echo`, prompting:
```text
macOS login password (grants Chrome Safe Storage access; entered once, never stored):
```
Requires `os.Stdin.Stat()` to report a character-device (PTY-allocated SSH session).
</ParamField>

<ParamField body="errNoInteractivePassword" type="error">
Returned when neither the env override nor an interactive terminal is available. Triggers the non-fatal downgrade to degraded described below.
</ParamField>

## Install paths

<Tabs>
<Tab title="Interactive SSH (default)">
```bash
ssh sink 'agentcookie wizard install --as sink ...'
```
You are prompted once for your macOS login password on the SSH TTY. The sink lands universal.
</Tab>
<Tab title="Non-interactive (CI)">
```bash
ssh sink 'AGENTCOOKIE_LOGIN_PASSWORD=… agentcookie wizard install --as sink ...'
```
No prompt fires. The env var is consumed by `acquireLoginPassword` and forwarded to `security -k`.
</Tab>
<Tab title="No password available">
```bash
ssh sink 'agentcookie wizard install --as sink ...'   # no TTY, no env var
```
Install **does not fail**. The keychain step downgrades non-fatally to **degraded** (sidecar + adapters still work) and prints the upgrade instruction:
```text
agentcookie wizard: WARNING universal keychain open did not complete: ...
agentcookie wizard:   downgrading this install to degraded ...
agentcookie wizard:   upgrade to universal over SSH with one password:
                       agentcookie wizard set-keychain-access
```
</Tab>
</Tabs>

The downgrade is governed by `resolveSinkDeliveryWithKeychain` in `internal/cli/wizard.go`. An explicit `--write-chrome-sqlite` forces universal and surfaces a warning instead of downgrading; an explicit `--skip-keychain-access` or `--skip-keychain-prompt` renders universal config without opening the keychain at all.

## Strategy routing reference

`keychainStrategyMode` in `internal/cli/wizard_keychain.go` selects between the inline path and the legacy LaunchAgent chain:

| Flags | Mode | Strategy used |
| --- | --- | --- |
| (none) | `inline` | `runInlinePartitionAccess` — one-password partition with `teamid:` |
| `--any-app` | `recreate` | `delete-and-recreate-with-A` (value-preserving) then T-list |
| `--recreate` | `recreate` | `delete-and-recreate-with-T` then `partition-list:apple-tool,apple` |
| `--extra-binary <path>` | adds `trust-list:<basename>` strategies in the recreate chain | — |
| `--skip-keychain-access` | (no open) | universal config rendered, keychain left untouched |

Inner strategies in the recreate chain are dispatched as a one-shot LaunchAgent via `RunOneShotLaunchAgent` in `internal/chrome/launchagent_helper.go` and probed after each step with `KeybaseKeychainProbe` (3-second cap) so a hung SecurityAgent prompt fails fast instead of burning ~30s per attempt.

## Narrow back

Nothing is deleted on the inline path, so there is no destructive rollback. To narrow access back to the security-CLI tools only (drop Dev-ID CGO readers), re-run the partition set without `teamid:`:

```bash
security set-generic-password-partition-list \
  -S "apple-tool:,apple:" -k "<login-password>" \
  -s "Chrome Safe Storage" -a Chrome
```

## Related pages

<CardGroup>
<Card title="Headless second-Mac install" href="/headless-install">
The SSH-only sink install flow, the degraded-mode fallback, and the `wizard set-keychain-access` upgrade path.
</Card>
<Card title="Cookie delivery surfaces" href="/cookie-delivery-surfaces">
The three sink-side delivery paths: real Chrome Default profile, the plaintext sidecar, and per-CLI adapter session files.
</Card>
<Card title="doctor and adapter verification" href="/doctor-health-checks">
The `Cookie delivery` check, duplicate-item race detection, and exit-code semantics for agent consumption.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Stuck-at-degraded sinks, `-25308 User interaction is not allowed`, and the duplicate-item race recovery sequence.
</Card>
</CardGroup>
