feat(523): forge-connection tokens are stored sealed under SECRET_KEY, like the backup S3 secret
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m54s
CI & Build / Build & push image (push) Successful in 36s

The column becomes token_sealed (migration 0122 seals existing rows with a
frozen copy of the v1 key), every reader opens it through connection_token,
a row this instance cannot open is skipped with a warning, and Settings asks
for that token again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 18:08:11 -04:00
co-authored by Claude Opus 5.5
parent 3008d29f89
commit 07bc51b890
10 changed files with 232 additions and 16 deletions
+26
View File
@@ -169,3 +169,29 @@ async def test_env_config_serves_admins_only_and_rows_beat_it(seeded):
assert len(selector.adapters) == 1
assert selector.adapters[0]._token == "row-tok"
await delete_connection(admin, row.id)
@pytest.mark.integration
async def test_the_token_is_stored_sealed_and_a_key_change_drops_the_row(seeded):
"""What a copy of the database holds is ciphertext; the reads still get
the token; and a row this instance can no longer open serves nothing
rather than sending ciphertext to the forge as a token."""
from sqlalchemy import text
from scribe.services import secrets
owner = seeded["owner"]
row = await create_connection(owner, kind="gitea", base_url=GITEA, token="plain-tok")
await update_connection(owner, row.id, token="rotated-tok")
async with async_session() as s:
stored = (await s.execute(
text("SELECT token_sealed FROM forge_connections WHERE id = :id"), {"id": row.id}
)).scalar_one()
assert "rotated-tok" not in stored
assert secrets.unseal(stored) == "rotated-tok"
selector = await get_forges(owner, seeded["project"])
assert [a._token for a in selector.adapters] == ["rotated-tok"]
with patch.object(secrets.Config, "SECRET_KEY", "a-different-instance"):
assert not (await get_forges(owner, seeded["project"])).configured