Two related install-time requests, both from running plannotator on several machines where the installer's defaults don't quite fit. Both currently need workarounds that I'd rather not maintain, and one of them is fragile in a way that will break silently.
Happy to open these separately if you'd prefer — they're grouped because they're both install.sh behaviour.
1. An explicit way to opt out of the Codex integration
What happens today: the installer auto-detects Codex and writes to ~/.codex/hooks.json and ~/.codex/config.toml. Detection is command -v codex or codex_home_has_user_config (any content under $CODEX_HOME).
There's no way to decline. --no-extras doesn't cover it, and there's no PLANNOTATOR_SKIP_CODEX_INSTALL to match the existing PLANNOTATOR_SKIP_SEM_INSTALL / PLANNOTATOR_SKIP_AGENT_TERMINAL_INSTALL.
Why that's a problem: ~/.codex/hooks.json isn't necessarily plannotator's to edit. On my machines it already holds my own pre_tool_use, user_prompt_submit and stop hooks, with other tooling that monitors it. I use plannotator via Claude Code and don't want the Codex integration at all — but I can't say so.
The workaround, which I'd like to delete: run the installer with /opt/homebrew/bin removed from PATH and CODEX_HOME pointed at an empty temp directory, so both detection legs fail:
SANDBOX=$(mktemp -d) && ln -s "$(command -v gh)" "$SANDBOX/gh"
env CODEX_HOME="$(mktemp -d)" PATH="$SANDBOX:/usr/bin:/bin:/usr/sbin:/sbin" \
bash install.sh --no-extras
It works — output reads Codex was not detected. — but it works by lying to the installer about the machine. The day a third detection leg is added, it fails silently: I'd get a Codex-wired install and the same reassuring "not detected" message. At roughly a release every three days, that's a real bet. I currently defend against it with a mandatory pre/post checksum assertion on both Codex files, which is a lot of machinery to avoid an env var.
It also, reasonably, trips security tooling: "execute a freshly downloaded script with a synthesised PATH shadowing a real binary" is a fair description of a malware installer. Mine flagged it, correctly.
Ask: PLANNOTATOR_SKIP_CODEX_INSTALL=1, or --skip-codex, matching the naming of the two skip vars that already exist. Same for the other agent integrations if it's cheap — Gemini, Kiro, OpenCode.
2. Provenance verification requires a credential it doesn't actually need
What happens today: install.sh verifies provenance with
gh attestation verify "$tmp_file" --repo "$REPO" --source-ref "refs/tags/$latest_tag" \
--signer-workflow ".../release.yml"
With no --bundle, gh requires an authenticated login. Without it the install fails closed — which is correct behaviour, and I'm glad it's fail-closed.
Why the credential is a real cost: this repo is public, and its attestations endpoint is world-readable:
D=$(shasum -a 256 ./plannotator | awk '{print $1}')
curl -s -o /dev/null -w '%{http_code}\n' \
"https://api.github.com/repos/<owner>/<repo>/attestations/sha256:$D"
# 200, no auth
Fetch the bundle and pass --bundle and the same verification passes with no credential at all — I've confirmed this under the full constraint set (--repo + --source-ref + --signer-workflow), with GH_CONFIG_DIR pointed at an empty directory and every GH_*_TOKEN unset, on three machines. Five negative controls (wrong binary, wrong owner, wrong source-ref, wrong signer-workflow, wrong repo) all correctly fail.
There's no security downgrade in doing it that way: the authenticated path fetches the bundle from the same endpoint. The only difference is an Authorization header on the same request to the same host.
The cost of requiring the login is concrete. On a headless or SSH-only machine, gh auth login can't reach the login keychain and stores the token in plain text, and the default web flow grants workflow scope — write access to .github/workflows/, i.e. arbitrary Actions execution with those repos' secrets. So a user who simply follows the install instructions on a remote box ends up with a broadly-scoped plaintext token on disk solely to read public data. That seems like a steep price for a verification step.
Ask: have install.sh fetch the attestation bundle from the public endpoint and verify with --bundle, falling back to the current path if the fetch fails. That would make verification work unauthenticated everywhere, and let users who don't want gh logged in keep provenance checking on rather than reaching for --skip-attestation.
Two things worth knowing if you implement it:
- The Sigstore trusted root is fetched via TUF on every run — not embedded, not cached (an empty
GH_CONFIG_DIR stays empty after a successful verify). So verification still needs network access and fails closed offline with no valid Sigstore verifiers could be initialized. Worth surfacing as a distinct message from "provenance failed", since they mean very different things.
- The unauthenticated API is 60 requests/hour per IP. Fine at install cadence, but shouldn't be retried in a loop.
For context on why I care about both: I run this with the plugin/hook deliberately not installed, invoked only by name, on machines where I need the install to be reproducible and the update path to be safe to run unattended. Both requests are about making the supported path fit that, rather than maintaining workarounds around it. The tool itself has been excellent — the annotate flow is exactly what I wanted.
Two related install-time requests, both from running plannotator on several machines where the installer's defaults don't quite fit. Both currently need workarounds that I'd rather not maintain, and one of them is fragile in a way that will break silently.
Happy to open these separately if you'd prefer — they're grouped because they're both
install.shbehaviour.1. An explicit way to opt out of the Codex integration
What happens today: the installer auto-detects Codex and writes to
~/.codex/hooks.jsonand~/.codex/config.toml. Detection iscommand -v codexorcodex_home_has_user_config(any content under$CODEX_HOME).There's no way to decline.
--no-extrasdoesn't cover it, and there's noPLANNOTATOR_SKIP_CODEX_INSTALLto match the existingPLANNOTATOR_SKIP_SEM_INSTALL/PLANNOTATOR_SKIP_AGENT_TERMINAL_INSTALL.Why that's a problem:
~/.codex/hooks.jsonisn't necessarily plannotator's to edit. On my machines it already holds my ownpre_tool_use,user_prompt_submitandstophooks, with other tooling that monitors it. I use plannotator via Claude Code and don't want the Codex integration at all — but I can't say so.The workaround, which I'd like to delete: run the installer with
/opt/homebrew/binremoved fromPATHandCODEX_HOMEpointed at an empty temp directory, so both detection legs fail:It works — output reads
Codex was not detected.— but it works by lying to the installer about the machine. The day a third detection leg is added, it fails silently: I'd get a Codex-wired install and the same reassuring "not detected" message. At roughly a release every three days, that's a real bet. I currently defend against it with a mandatory pre/post checksum assertion on both Codex files, which is a lot of machinery to avoid an env var.It also, reasonably, trips security tooling: "execute a freshly downloaded script with a synthesised
PATHshadowing a real binary" is a fair description of a malware installer. Mine flagged it, correctly.Ask:
PLANNOTATOR_SKIP_CODEX_INSTALL=1, or--skip-codex, matching the naming of the two skip vars that already exist. Same for the other agent integrations if it's cheap — Gemini, Kiro, OpenCode.2. Provenance verification requires a credential it doesn't actually need
What happens today:
install.shverifies provenance withWith no
--bundle,ghrequires an authenticated login. Without it the install fails closed — which is correct behaviour, and I'm glad it's fail-closed.Why the credential is a real cost: this repo is public, and its attestations endpoint is world-readable:
Fetch the bundle and pass
--bundleand the same verification passes with no credential at all — I've confirmed this under the full constraint set (--repo+--source-ref+--signer-workflow), withGH_CONFIG_DIRpointed at an empty directory and everyGH_*_TOKENunset, on three machines. Five negative controls (wrong binary, wrong owner, wrong source-ref, wrong signer-workflow, wrong repo) all correctly fail.There's no security downgrade in doing it that way: the authenticated path fetches the bundle from the same endpoint. The only difference is an
Authorizationheader on the same request to the same host.The cost of requiring the login is concrete. On a headless or SSH-only machine,
gh auth logincan't reach the login keychain and stores the token in plain text, and the default web flow grantsworkflowscope — write access to.github/workflows/, i.e. arbitrary Actions execution with those repos' secrets. So a user who simply follows the install instructions on a remote box ends up with a broadly-scoped plaintext token on disk solely to read public data. That seems like a steep price for a verification step.Ask: have
install.shfetch the attestation bundle from the public endpoint and verify with--bundle, falling back to the current path if the fetch fails. That would make verification work unauthenticated everywhere, and let users who don't wantghlogged in keep provenance checking on rather than reaching for--skip-attestation.Two things worth knowing if you implement it:
GH_CONFIG_DIRstays empty after a successful verify). So verification still needs network access and fails closed offline withno valid Sigstore verifiers could be initialized. Worth surfacing as a distinct message from "provenance failed", since they mean very different things.For context on why I care about both: I run this with the plugin/hook deliberately not installed, invoked only by name, on machines where I need the install to be reproducible and the update path to be safe to run unattended. Both requests are about making the supported path fit that, rather than maintaining workarounds around it. The tool itself has been excellent — the annotate flow is exactly what I wanted.