feat(infra): own livestore-docs Netlify runtime env via OpenTofu (seeds #1244) - #1330
Merged
Merged
Conversation
19 tasks
Contributor
PR preview
Report historyPR 1330 · 2026-07-27 22:32 UTC
PR 1330 · 2026-07-27 21:52 UTC
PR 1330 · 2026-07-27 20:15 UTC
PR 1330 · 2026-07-27 20:10 UTC
PR 1330 · 2026-07-27 19:36 UTC
PR 1330 · 2026-07-27 12:35 UTC
PR 1330 · 2026-07-27 12:22 UTC
PR 1330 · 2026-07-27 12:07 UTC
|
schickling-assistant
force-pushed
the
schickling-assistant/2026-06-15-netlify-iac
branch
from
July 27, 2026 11:23
2cd78c0 to
34e6ec9
Compare
schickling-assistant
force-pushed
the
schickling-assistant/2026-06-15-netlify-iac
branch
from
July 27, 2026 11:38
34e6ec9 to
f8a94cd
Compare
schickling-assistant
force-pushed
the
schickling-assistant/2026-06-15-netlify-iac
branch
from
July 27, 2026 11:52
f8a94cd to
96e7aa6
Compare
schickling
marked this pull request as ready for review
July 27, 2026 19:30
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e92545df65
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
schickling
approved these changes
Jul 27, 2026
#1244) Re-home the livestore-docs Netlify env-var IaC into this repo under `.infra/iac/netlify/` with OpenTofu, adopting the two already-live env vars (`MXBAI_API_KEY`, `MXBAI_VECTOR_STORE_ID`) via `tofu import` so the config owns them with zero recreation. `tofu plan` reports `No changes`. Scope is env vars only: the site is referenced by literal `site_id`/`team_id` (no `netlify_site` resource / data source), so OpenTofu can never mutate the site, build settings, or deploys. State backend: this repo is public and state holds the API key value, so state is committed only as ciphertext via OpenTofu native state encryption (PBKDF2/AES-GCM, `enforced = true`; passphrase in 1Password, injected at runtime). The intended Cloudflare R2 `backend "s3"` is blocked — R2 is not enabled on the LiveStore CF account (API code 10042, dashboard-only) — so native encryption is the sanctioned fallback; `versions.tf` documents the R2 migration. `MXBAI_API_KEY` declares `lifecycle { ignore_changes = [secret_values] }` because the Netlify API treats secret values as write-only (never returned on read/import), which would otherwise force a perpetual cosmetic diff. Adds `dt infra:netlify:plan` / `dt infra:netlify:apply` tasks that inject secrets via op-proxy/1Password (or env in CI) and run OpenTofu. Seeds #1244 (LiveStore public domains -> Cloudflare-managed IaC single desired state) by establishing the OpenTofu state + encryption + provider conventions. Refs #1329. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> agent-session-id: c5481079-88d4-4406-b2bf-d31ff5588e67 agent-tool: Claude Code agent-tool-version: 2.1.165 agent-model: claude-opus-4-8 agent-runtime-profile: /nix/store/ffppqrb9xkq0jamayzc05dwlm86yi5yk-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/f4bp1wnsfzypdxckkp0vyqcgw5xq4yp1-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b39be89
Adds context/03-delivery/04-infrastructure (LS.DEL.INFRA-*): declared infrastructure, no recoverable secrets in-repo, 1Password as the single credential source, adoption over recreation, org-owned accounts. The requirements state the target, so this PR's own compromises are tracked as deltas rather than written down to what the code happens to do: DELTA-001 (state committed as ciphertext to a public repo, blocked on R2) and DELTA-002 (hosting, DNS, CI runners and examples are undeclared). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Observed 2026-07-27: a secret Netlify env var returns its `dev` context in plaintext while other contexts are masked. Masking-on-read is what makes the value non-round-trippable, not a write-only guarantee. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Import writes non-secret values into state verbatim, so a credential a provider holds as ordinary configuration must be moved to that provider's secret mechanism before adoption, not after. Also drops an over-specific observation from the decision record; the consequence is what matters. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Observed 2026-07-27: one vector store exists, named for development, last updated 2026-04-27 with 31 content files changed since. Both docs surfaces read it, sync-docs.yml has failed at Setup pnpm on every push for ~3 months, and MXBAI_VECTOR_STORE_ID_PROD names no existing store. The dev/prod search split exists only in the write path: no surface reads a store its own sync path writes. Adopting this into declared state before repairing it would freeze the crossed wiring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Decrypting the committed state on 2026-07-27 showed both imported resources carry no secret material; the only value present is the non-secret vector store id, already a literal in variables.tf. LS.DEL.INFRA-R02 is satisfied, so DELTA-001 recorded a divergence that does not exist and is removed. What replaces it is the rule it was groping at: state carries no secret, and remote state is required *before* the first secret-bearing resource is declared, not after — afterwards the commit is already permanent. R2's account-level enablement constraint moves to .reference/ so #1244 inherits it, including the ordering hazard that its own Cloudflare work would both trip the trigger and provide the bucket. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
LS.DEL.INFRA-R06: a credential in a provider surface uses that provider's secret facility, never ordinary configuration — plain config leaks into build logs and read APIs, and is unsafe to adopt into declared state. LS.DOCS.SEARCH-R03: a docs surface serves the index its own sync path writes. Measured 2026-07-27: 24 of 101 content files differ between v0.4.0 and main, so a shared index leaves one surface wrong about a quarter of the docs whichever way it syncs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
The production surface now has its own vector store, so the declared value moves to it and the config states that the dev surface is deliberately not managed here. Corrects two claims the evidence contradicts. State does not hold the API key: the resource is adopt-only, and decrypting the committed state shows the only value present is the non-secret store id. And rotation does not run through OpenTofu — applying one would write the key into permanently public history; 1Password is canonical. R2 is reframed from a blocker to a trigger: state must leave the repo before the first secret-bearing resource is declared, not on a schedule. tofu plan reports No changes against the corrected wiring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Netlify returns a secret variable's dev-context value in plaintext while masking the others, reproduced on both docs sites — so the dev context is not a safe place for a credential, and masking only guarantees a value cannot be round-tripped. Non-secret values are returned verbatim, which is the concrete reason behind LS.DEL.INFRA-R06. Also corrects the search delta: its staleness claim rested on the store's updated_at, which tracks metadata rather than file operations. The failing sync runs are the real evidence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
The workflow fix ships as #1507 so a three-month production defect is not gated on review of the state-backend design. Records that main still passes the legacy store id to the production sync until that merges. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Both surfaces now return results from their own store. What remains is that the production sync path has never run successfully — the production store was populated by hand — and that a failing sync still surfaces nowhere, which is why the original bug survived three months. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Resolving the mono-wrappers.nix conflict by stripping markers dropped the closing '' of docsSearchSync, so the IaC preamble's opening '' terminated it instead and the shell body parsed as Nix. Caught by check:quick. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Extends the declaration from one site to both, keyed by surface, so the dev site's env vars stop being undeclared. Adopting it is only safe now because its API key was moved from plain configuration to Netlify's secret mechanism first (LS.DEL.INFRA-R06) — importing it beforehand would have written a live credential into state. Adds infra:netlify:drift-check and a scheduled workflow that runs it. A declaration nobody checks is documentation, not a control: nothing in the repo ran plan, so drift was undetectable by design. Proven end to end against the live account: import, plan reported real drift (the dev store-id variable carried an undeclared post-processing scope), apply converged it, and plan is clean. State still contains no secret after that apply. Also widens the ignore rules — `tofu state mv` writes `terraform.tfstate.<ts>.backup`, which `*.tfstate.backup` did not match, so those files sat untracked and one commit away from being added. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Extends the declaration from environment variables to the rest of what this Netlify account actually governs: the eleven hand-authored records in the livestore.dev zone, plus the custom domains of the two docs surfaces. Seventeen resources, all adopted by import, plan reports No changes. Scope is deliberately the hand-authored records only. The zone also holds NETLIFY-type records that Netlify creates and owns from each site's domain settings; the provider cannot express that type at all, so they are governed through netlify_site_domain_settings rather than declared directly. There is no netlify_dns_zone resource either — the zone is referenced by literal id so OpenTofu can never create, replace, or delete it. Most of these records serve a live Clerk instance with no consumer anywhere in this repository. They are declared because they are load-bearing, not because their purpose is understood. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
The web-todomvc-custom-elements example was removed from the repository, but its two Netlify sites kept their livestore.dev domain claims, and a claim is what makes Netlify publish the matching zone record. Adopted at current values so the adoption changes nothing; releasing them is a separate diff. This config owns the livestore.dev namespace, so a hostname under it is in scope wherever it is driven from. An unowned claim is how the namespace drifted in the first place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
Both custom-elements domain claims are released and Netlify withdrew the records it had published for them; the record that had outlived its site claim is deleted. Zero orphaned hostnames remain in the zone. The release could not be expressed in the config. The provider cannot say "no custom domain" — null is silently omitted and an empty string is rejected with `Name is blank` — so it went through the Netlify API, and the config now asserts the absence by declaring the resources without a domain. That keeps the absence checked rather than merely achieved. Chasing this surfaced two provider gaps worth recording rather than rediscovering: a desired state the provider cannot reach would leave the scheduled drift check permanently red, and NETLIFY-type records cannot be declared at all, so a record orphaned from its claim is invisible to drift detection. Records LS.DEL.INFRA-DQ2: ten of the eleven hand-authored records serve a live Clerk instance with no consumer anywhere in this repository. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
The intent-layer test requires every ID namespace defined under context/ to appear in the spec.md table that maps namespaces to owning directories. Adding the infrastructure node introduced nine LS.DEL.INFRA-* ids without registering the namespace, so test-unit failed with nine violations. Worth noting for next time: check:quick does not run tests — its description ends "without tests" — so this class of failure is invisible locally unless test:unit or check:all is run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
…ribute Two review findings, both correct. The canonical documents still described an env-vars-only blast radius after DNS records and site domain settings were added, so an operator reading them would have underestimated what infra:netlify:apply can modify. The decision record, env.tf, README, and DELTA-002 now describe the real scope. The prod-safety claim is restated as what it actually is: resources are declared, containers are not — no site resource, no zone resource, no data sources — so destroying a container is not expressible. `ignore_changes = [secret_values]` suppresses the entire attribute, not only the write-only values, so the context entries stopped being compared too. Verified by deleting a live context: `tofu plan` reported `No changes`, while docs search would have broken on the next build. The drift task now asserts the ignored shape against the provider API after planning — still a secret, with the expected scopes and contexts, on both surfaces. Confirmed it fails on that injected drift and passes once restored. Only the values remain unverifiable, which is inherent to write-only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> agent-session-id: 05bdf052-d9c2-4930-bfef-1fd90b43680b agent-tool: Claude Code agent-tool-version: 2.1.220 agent-model: claude-opus-5 agent-runtime-profile: /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@b785b9f
schickling-assistant
force-pushed
the
schickling-assistant/2026-06-15-netlify-iac
branch
from
July 27, 2026 21:55
3d6e749 to
8081ce0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased onto
main(was 343 commits behind). What began as re-homing two Netlify env vars into OpenTofu grew: verifying the adoption surfaced a production defect in docs search (repaired separately in #1507), and then investigating what else this account governs surfaced real drift in thelivestore.devnamespace.What is declared
19 resources on the
livestoreNetlify account, all adopted by import —tofu planreportsNo changes.netlify_environment_variablenetlify_dns_recordlivestore.devzonenetlify_site_domain_settingsProd-safety is structural. There is no
netlify_siteresource, no site data source, and nonetlify_dns_zoneresource — sites and the zone are referenced by literal id, so OpenTofu can never create, replace, or delete them. Records are additive and individually replaceable; a zone is not.Drift detection, not documentation
Nothing in the repo ran
plan, so drift was undetectable by design. Addedinfra:netlify:drift-check(plan -detailed-exitcode, so drift exits non-zero) and.github/workflows/infra-drift.yml— weekday schedule plus any push touching.infra/iac/**. Deliberately notpull_request: fork PRs get no secrets and would fail for reasons unrelated to drift.Turning it on immediately found drift that had accumulated while nothing was watching:
post-processingscopetodomvc-custom-elements.livestore.dev— a site claiming a hostname with no zone record behind it, so it never resolvedlinearlite.livestore.dev— a record that outlived the site claim that created it, pointing at a site serving 404All three are resolved. The example those hostnames belonged to was deleted from the repo some time ago; its infrastructure outlived it. Zero orphaned hostnames remain.
Secrets
The declaration owns the API key's shape, never its value. Netlify masks secret values on read, so an imported secret carries no usable value;
ignore_changes = [secret_values]keeps it that way. Rotation happens in 1Password, not through OpenTofu — applying one here would write the key into permanently public history.On committed state: the repo is public, so the rule is that state contains no secret. It doesn't — verified by decrypting it, including after a real
apply. Encryption (PBKDF2-SHA512 / AES-GCM, generated passphrase) is defence in depth against a future mistake, not what makes this safe.R2 is therefore not a blocker but a trigger: state must leave the repo before the first resource whose state carries secret material is declared, because a commit cannot be retracted. Since #1244 needs the same Cloudflare account work, that should happen before it rather than during it.
Two provider limits worth knowing
Recorded in
.reference/netlify-provider-limitations.mdso they aren't rediscovered:nullis silently omitted (apply "succeeds", changes nothing, and every later plan is dirty);""is rejected withName is blank. The API can do it, so the release was a direct API call and the config asserts the absence by declaring the resource without a domain.NETLIFY-type records cannot be declared at all — the provider's type list excludes them. They're governed indirectly by owning the claims that produce them, which leaves a real gap: a record orphaned from its claim is invisible to drift detection. That is exactly howlinearlite.livestore.devsurvived.VRS
Adds
context/03-delivery/04-infrastructure/(LS.DEL.INFRA-*): declared infrastructure, no recoverable secrets in-repo, 1Password as the single credential source, adoption over recreation, org-owned accounts (now verified — all sites are on thelivestoreteam, not a personal account), and credentials using the provider's secret mechanism.Requirements state the target, so remaining gaps stay visible as deltas rather than being written down to what the code does.
LS.DEL.INFRA-DQ2records the one thing this work could not explain: ten of the eleven hand-authored DNS records serve a live Clerk instance that nothing in this repository references.🤖 Generated with Claude Code
Posted on behalf of @schickling
agent_nameagent_session_idagent_toolagent_tool_versionagent_runtimeagent_modelruntime_profileskills_manifestworktreemachinetooling_profile