refactor(secrets): consumers read the store; drop materialized copies

Materializing wrote a second plaintext file per secret, which is the problem the store
was meant to solve: two files drift, and the copy is what ends up committed or grepped.

- tangled_pr / tangled_pr_edit / tangled_repo now read tangled.cookie from the store.
  engine/.tangled-session is deleted; --cookie-file remains as a legacy escape hatch.
- materialize() is replaced by run-time injection that leaves nothing at rest:
    exec-env <group> -- cmd      group as env vars (use this instead of a systemd
                                 EnvironmentFile — same effect, no plaintext on disk)
    with-file <key> -- cmd {}    0600 file in a private tmpdir, removed when cmd exits,
                                 for consumers that insist on a path (ssh -i, a TLS key)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-01 16:58:26 +00:00
co-authored by Claude
parent 6c56c1953e
commit 300e69d3b3
5 changed files with 110 additions and 67 deletions
+15 -9
View File
@@ -13,14 +13,20 @@ import argparse, os, sys, urllib.parse, urllib.request
BASE = "https://tangled.org"
def load_cookie(path):
if not os.path.exists(path):
sys.exit(f"no cookie file at {path} — refresh it with scripts/get-tangled-cookie.py")
for line in open(path):
line = line.strip()
if line.startswith("TANGLED_COOKIE="):
return line[len("TANGLED_COOKIE="):]
sys.exit("cookie file present but has no TANGLED_COOKIE= line")
def load_cookie(path=None):
"""The cookie lives in the encrypted store (tangled.cookie) — see engine/README.md (Secrets).
`path` is a legacy escape hatch: a TANGLED_COOKIE=... file, used only if explicitly passed."""
if path:
for line in open(path):
if line.strip().startswith("TANGLED_COOKIE="):
return line.strip()[len("TANGLED_COOKIE="):]
sys.exit(f"{path} has no TANGLED_COOKIE= line")
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import secrets as _store
c = _store.get("tangled.cookie")
if not c:
sys.exit("no tangled.cookie in the secret store — add it with: sops /secrets/store.yaml")
return c
def main():
ap = argparse.ArgumentParser(description="create a Tangled repo via a reused session cookie")
@@ -28,7 +34,7 @@ def main():
ap.add_argument("--description", default="")
ap.add_argument("--branch", default="main", help="default branch (form default: main)")
ap.add_argument("--domain", default="knot1.tangled.sh", help="knot to host on (radio value)")
ap.add_argument("--cookie-file", default=os.path.join(os.path.dirname(os.path.abspath(__file__)), ".tangled-session"))
ap.add_argument("--cookie-file", default=None, help="legacy: read the cookie from this file instead of the store")
a = ap.parse_args()
cookie = load_cookie(a.cookie_file)