Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion reports/technical_risk_register.md
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ It is a worked example wearing a risk's clothes. **Give it a real trigger or mov
| ID | C-84 |
| Tier | 2 — not silent. The delivery fails loudly and completely, which is the correct behaviour and also the whole problem: there is no degraded mode, no fallback identity, and the date is known in advance. A foreseeable total outage that nobody has scheduled work against is a structural risk, not an operational surprise. |
| Source | views-appwrite coordinate registry v1.4.3/v1.4.4 — operator console read, 2026-08-05 (þing-02 A3(i)) |
| Trigger | **A date, unusually — 2026-11-17.** The registry records `VIEWS Pipeline Core` expiring 12:35 and `UN FAO` 16:10 that afternoon. Act when the un_fao delivery is next scheduled within a month of it, or when anyone plans a rotation, whichever is first. |
| Trigger | **A date, unusually — 2026-11-17.** The registry records `VIEWS Pipeline Core` expiring 12:35 and `UN FAO` 16:10 that afternoon. Act when the un_fao delivery is next scheduled within a month of it, or when anyone plans a rotation, whichever is first. **Since 2026-08-19 the first arm fires by itself**: `tests/test_credential_expiry.py` goes red from 2026-10-18, so the date no longer depends on anyone remembering it. |
| Owner | Simon, and only Simon — issuing and installing keys is a console action. This entry exists so the date is visible from *this* repo's planning surface rather than only from the platform's. |
| Location | `views_postprocessing/{unfao,crafd}/appwrite_env.py` — the declared coordinates; the values live in the environment and the registry, never here. |

Expand All @@ -712,6 +712,12 @@ The FAO delivery authenticates with the `UN FAO` key. That key expires **2026-11

**Deliberately not fixed here, and the reason is C-84's own shape.** A preflight that checks key validity means an authenticated call at startup, and the only project to make it against is production — which **þing-01 D2** forbids for tests and this would not quite be — and which that verdict explicitly permits as *read-only preflight validation*, so the obstacle here is the authenticated call, not the prohibition (see C-95). The honest position is that this is a *date to act on*, not a mechanism to build, and inventing a mechanism would be building the wrong thing to feel busy. Registered so the date is not discovered by an outage.

**The trigger now fires on its own (2026-08-19), and this is not the mechanism above.** The first arm of the trigger — *"act when the un_fao delivery is next scheduled within a month of it"* — was a trigger nobody could notice: it fired in someone's memory or not at all, which is the same defect that withdrew the third arm of C-94's trigger and which ADR-014 §4 exists to forbid. `tests/test_credential_expiry.py` declares the two expiries and fails from 30 days out, naming the dates, the 3h35m gap, who owns the rotation (operator; views-appwrite#12, key split views-faoapi#338), and the two honest ways to make it pass — rotate and update the constant, or update the constant if a key was replaced early.

**It is emphatically not the key-validity preflight this entry rejected.** No authenticated call, no credential, no network — a calendar and two declared datetimes. The rejection above stands and is unaffected: what was wrong was building a mechanism to *discover* a fact already known; what was missing was making the known fact impossible to forget. **The acknowledgement is the load-bearing part, and the first draft did not have it.** `/code-review high` found two design faults that would each have ended with the test deleted. (a) A literal pin on the two datetimes made the tripwire's own prescribed remediation — *rotate, then update `KEY_EXPIRY`* — fail a second test whose message said not to adjust the constant. A guard that refuses its own documented fix is worse than no guard, and it would have landed on the one person who could not route around it. The pin is gone. (b) From 2026-10-18 the gate would have been red for **every unrelated pull request**, clearable only by an operator console action the repository cannot perform — which is precisely what `pyproject.toml` says about ruff, citing ADR-014 §3: *a gate that starts red gets switched off*. `ACKNOWLEDGED_UNTIL` is the in-repo escape: a declared, reviewed, dated edit meaning *seen, and being acted on*, which **cannot be set on or after the expiry** — so it postpones attention and can never replace it.

Four companion tests keep it honest rather than decorative: the firing branch is exercised against a probe **derived from** `KEY_EXPIRY` (**C-102** — a guard that has never run is unproven; deriving it rather than hardcoding means the proof survives a rotation instead of quietly expiring with it); an acknowledgement past the expiry is refused; `LEAD_DAYS` is floored, because shaving a week off the warning neuters the guard while leaving it looking present; and the outage-day text is checked, since the first draft would have told an operator the keys expired *"in -3 days"* while the seam was down. All verified by mutation.

Cross-refs: **C-81** (the same operator session's other half — branch protection and the CI token), **C-27** (no rotation mechanism for a secret value upstream), **C-57** (the pinned-registry detector, which is how this arrived here at all — it demanded the v1.4.4 bump and the bump is what surfaced the expiry), þing-02 A3(i), views-appwrite C-65 and C-66.

---
Expand Down
182 changes: 182 additions & 0 deletions tests/test_credential_expiry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
"""A dated tripwire for the platform key expiry (register C-84, issue #224).

**What this is not.** C-84 considered a key-validity preflight and rejected it: *"this is
a date to act on, not a mechanism to build, and inventing a mechanism would be building
the wrong thing to feel busy."* That verdict stands. There is no authenticated call here,
no credential, no network — only a calendar.

**What this is.** C-84's trigger reads *"act when the un_fao delivery is next scheduled
within a month of it"*. That is a trigger nobody can notice: it fires in someone's memory
or not at all, which ADR-014 §4 forbids and which withdrew the third arm of C-94's
trigger. The date is known 90 days ahead and the consequence is a total outage of every
identity on the seam, so the trigger is made to fire on its own.

**The acknowledgement is the load-bearing part, and the first draft did not have it.**
A gate that goes red on a date, cannot be cleared from inside the repository, and blocks
every unrelated pull request is a gate that gets deleted — `pyproject.toml` says exactly
that about ruff, citing ADR-014 §3. Rotation is an operator console action this repo
cannot perform, so without an in-repo escape the tripwire would hold the merge queue
hostage from 2026-10-18 until someone with console access acted. ``ACKNOWLEDGED_UNTIL``
is that escape: a declared date that says *we have seen this and will act by then*. It is
a deliberate, reviewed, dated edit — and it **cannot be set past the expiry**, so it can
postpone attention but never replace it.

**There is deliberately no test pinning these datetimes to literals.** The first draft had
one, and it made the remediation the tripwire itself prescribes — *rotate, then update
``KEY_EXPIRY``* — fail a second test whose message said not to adjust the constant. A
guard that refuses its own documented fix is worse than no guard, and it would have landed
on the one person who could not route around it.
"""

from __future__ import annotations

from datetime import date, datetime, timedelta

import pytest

#: The platform keys this repository's deliveries authenticate with, and when they die.
#: Read from the operator console 2026-08-05 (þing-02 A3(i)); recorded in views-appwrite
#: coordinate registry v1.4.4 and in register C-84. Both of this repo's paths — the FAO
#: delivery and the CRAF'd delivery — run under `VIEWS Pipeline Core`, the earlier one.
#:
#: Naive datetimes, deliberately: the console reports local time and the window below is
#: 30 days, so an hour either way changes nothing. Adding tzinfo would imply a precision
#: the source does not have.
KEY_EXPIRY: dict[str, datetime] = {
"VIEWS Pipeline Core": datetime(2026, 11, 17, 12, 35),
"UN FAO": datetime(2026, 11, 17, 16, 10),
}

#: How long before the earliest expiry this starts failing. C-84's own window, and the
#: time a rotation needs to be scheduled with an operator rather than squeezed in.
LEAD_DAYS = 30

#: Set to a date to silence the tripwire until then — *"seen, and being acted on"*.
#: Must be before the expiry (asserted below), so it postpones attention rather than
#: removing it. ``None`` means unacknowledged.
ACKNOWLEDGED_UNTIL: date | None = None


def days_until_earliest_expiry(today: date) -> tuple[str, int]:
"""(which key dies first, days until it does) — a pure function of a date.

Split out so the FAILING branch can be exercised in the suite. A tripwire whose
firing path has never run is unproven however carefully it was written (C-102), and
this one would otherwise first execute live in October, on the day it matters.
"""
name = min(KEY_EXPIRY, key=lambda k: KEY_EXPIRY[k])
return name, (KEY_EXPIRY[name].date() - today).days


def expiry_warning(name: str, days_left: int) -> str:
"""What the tripwire says when it fires."""
spread = max(KEY_EXPIRY.values()) - min(KEY_EXPIRY.values())
hours, remainder = divmod(int(spread.total_seconds()), 3600)
gap = f"{hours}h{remainder // 60:02d}m"
when = (
f"in {days_left} days" if days_left > 0
else "TODAY" if days_left == 0
else f"{-days_left} days ago — the seam is already dead"
)
listing = "\n".join(
f" {key:22} {stamp:%Y-%m-%d %H:%M}"
for key, stamp in sorted(KEY_EXPIRY.items(), key=lambda kv: kv[1])
)
return (
f"the platform Appwrite keys expire {when}:\n{listing}\n\n"
f"Both of this repository's delivery paths — un_fao and crafd — authenticate with "
f"{name!r}, the earlier one. The two keys are {gap} apart, which is not a stagger: "
"neither can carry traffic while the other is replaced, so a rotation that assumes "
"a window has none. After the later time every identity on the seam is dead at "
"once — model and ensemble writes, both partner deliveries, and FAO's own read "
"access.\n\n"
"This repository cannot rotate anything and must not hold credentials "
"(þing-01 D3). Issuing and installing keys is an operator console action "
"(views-appwrite#12); the key split is views-faoapi#338.\n\n"
"THREE ways to make this pass, in order of preference:\n"
" 1. rotate the keys, then update KEY_EXPIRY to the new expiries;\n"
" 2. if a key was replaced early, update KEY_EXPIRY to match;\n"
" 3. set ACKNOWLEDGED_UNTIL to a date before the expiry — this says the rotation "
"is scheduled and stops the tripwire blocking unrelated work until then.\n"
"Deleting this test is the fourth way and it is the one that produces the outage "
"(register C-84, issue #224)."
)


def test_the_platform_keys_are_not_about_to_expire():
"""Fails inside the lead window unless the date is explicitly acknowledged."""
today = date.today()
name, days_left = days_until_earliest_expiry(today)
if days_left > LEAD_DAYS:
return
if ACKNOWLEDGED_UNTIL is not None and today <= ACKNOWLEDGED_UNTIL:
return
pytest.fail(expiry_warning(name, days_left))


def test_an_acknowledgement_cannot_outlive_the_expiry():
"""The escape hatch postpones attention; it must not be able to remove it.

An open-ended acknowledgement is just the deletion in finding 4's clothing, and it
would read as a live guard while being none.
"""
if ACKNOWLEDGED_UNTIL is None:
return
earliest = min(KEY_EXPIRY.values()).date()
assert ACKNOWLEDGED_UNTIL < earliest, (
f"ACKNOWLEDGED_UNTIL is {ACKNOWLEDGED_UNTIL}, on or after the earliest expiry "
f"({earliest}). An acknowledgement that outlives the thing it acknowledges is a "
"silent deletion — the suite would stay green straight through the outage."
)


def test_the_tripwire_actually_fires_inside_the_lead_window():
"""The failing branch, exercised now rather than first executing in October (C-102).

The probe date is DERIVED from `KEY_EXPIRY`, so this keeps proving something after a
rotation. Hardcoding it — as the first draft did — meant the proof quietly expired
the moment the constant was legitimately updated.
"""
earliest = min(KEY_EXPIRY.values()).date()
probe = earliest - timedelta(days=LEAD_DAYS - 7)
name, days_left = days_until_earliest_expiry(probe)

assert name == "VIEWS Pipeline Core", "the earlier key is the one both paths use"
assert 0 < days_left <= LEAD_DAYS, "the probe must sit inside the window it tests"

message = expiry_warning(name, days_left)
assert f"expire in {days_left} days" in message
assert "3h35m apart" in message, "the gap is C-84's finding, not the dates"
assert "not a stagger" in message
assert "views-appwrite#12" in message, "the reader must be told who can act"
assert "ACKNOWLEDGED_UNTIL" in message, "and how to clear it without deleting it"
assert "Deleting this test" in message, (
"the last option must be named, or it is the one that gets taken quietly"
)


def test_the_tripwire_is_silent_outside_the_lead_window():
earliest = min(KEY_EXPIRY.values()).date()
_, days_left = days_until_earliest_expiry(earliest - timedelta(days=LEAD_DAYS + 60))
assert days_left > LEAD_DAYS, "a tripwire that is always red is one nobody reads"


def test_the_lead_window_is_not_quietly_shrunk():
"""Shaving days off `LEAD_DAYS` neuters this without deleting anything.

A floor rather than an equality: widening the window is always safe, and pinning the
exact value would recreate the problem the removed literal-pin caused.
"""
assert LEAD_DAYS >= 30, (
f"LEAD_DAYS is {LEAD_DAYS}. C-84's window is a month, and rotation needs an "
"overlap period scheduled with an operator — shortening the warning is how a "
"dated guard gets neutered while still looking present."
)


def test_the_outage_day_message_still_reads(tmp_path):
"""The text an operator reads while the seam is down must not say '-3 days'."""
earliest = min(KEY_EXPIRY.values()).date()
for probe, expected in ((earliest, "TODAY"), (earliest + timedelta(days=3), "3 days ago")):
name, days_left = days_until_earliest_expiry(probe)
assert expected in expiry_warning(name, days_left)
Loading