rouse-context/docs
monkopedia-coder 3e80f49d31
Some checks are pending
CI / Lint (push) Waiting to run
CI / Build & Test (push) Waiting to run
Relay CI / relay-test (push) Waiting to run
relay: throttle wrong integration secrets per (IP, subdomain) (#811) (#819)
* relay: tests for wrong-secret throttle per (IP, subdomain) (#811)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: throttle wrong integration secrets per (IP, subdomain) (#811)

After 5 invalid secrets within an hour from one IP on one subdomain, the
relay closes that IP on that subdomain for the rest of the window, also
when the secret is correct, and never wakes the device for a blocked
attempt. Thresholds and the entry cap live under [limits]; state is an
in-memory, capped, swept table, separate from the 200/min connection
limiter.

Docs drop the ~4M-combinations claim: the secret is a filter against
casual scanning, and OAuth approval on the phone is the real gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: regression tests for concurrent wrong-secret bursts (#811)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: reserve wrong-secret slots before the secret lookup (#811)

The throttle checked, awaited the secret lookup, then recorded, so a
concurrent burst all passed the check during a cold-cache or legacy
secret_prefix lookup. Admission now reserves a slot under the map entry
before the await and settles it right after validation, before any wake.
In-flight distinct secrets count against the budget; connections with
the same secret share a slot so a real client's burst is not refused.
The window anchors at the first failure, idle entries are dropped, the
full-table sweep runs at most once a second, and a block is logged at
info with IP and subdomain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: tests for retired secrets not counting toward the throttle (#811)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: retired secrets do not count toward the wrong-secret throttle (#811)

A stale client config (e.g. an old connector after a disable/re-enable
rotated its secret) retrying from a shared egress IP must not lock out
the working URL. An exact match for one of the device's retired secrets
is still closed like any invalid secret, but its reserved slot is
released without counting. The check runs inside the same reserve/settle
flow, so it cannot widen a concurrent burst. The secrets cache now
carries the retired set alongside valid_secrets (filled from the same
Firestore lookup and by /rotate-secret), so no extra reads are added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: tests for stale secrets across re-enable and shared-slot failures (#811)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: stale_secrets list for the throttle exemption; sticky shared-slot failure (#811)

The retired-secret exemption missed re-enable: /rotate-secret clears a
re-enabled integration from retired_secrets, so the old connector's URL
counted again and could lock out the new one. Former secrets now go to a
separate stale_secrets list on the device record, used only for the
exemption: every secret that stops being valid (disable, replacement on
re-enable, same-subdomain re-registration) is appended, at most 16 kept,
oldest dropped. It is persisted with the record, cached with
valid_secrets from the same lookup and from /rotate-secret and
/register, and reads empty on old records. retired_secrets keeps its
#519 role unchanged. Valid secrets are checked first.

A failure on a shared slot is now sticky, so a wrong guess still counts
when the last sharer is dropped unsettled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: tests for pre-deploy retired secrets staying exempt (#811)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

* relay: exempt retired secrets from the throttle alongside stale ones (#811)

Integrations disabled before stale_secrets existed only have their
former secret in retired_secrets. The exemption set is now stale_secrets
plus the retired values, read from the same lookup, so those stay exempt
with no migration. retired_secrets semantics (#519) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYSPuh9bZEJkrzvnEpY1QK

---------

Co-authored-by: monkopedia-coder <monkopedia-coder@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 06:01:03 -05:00
..
auth-pages Regenerate auth page gallery with correct icon 2026-04-08 08:53:44 -04:00
design relay: throttle wrong integration secrets per (IP, subdomain) (#811) (#819) 2026-11-01 06:01:03 -05:00
internal Catch braced interpolation in the sensitive-logging gate (#692) (#695) 2026-09-03 00:43:22 -04:00
notifications Fix #347: Client-subject notification copy + deep-link taps + Manage action (#362) 2026-04-22 03:11:08 -04:00
screenshots Keep Health Connect setup on screen after the base grant (#537) (#610) 2026-08-26 14:01:43 -04:00
user docs(privacy): name what actually leaves the device (#743) 2026-09-09 23:31:30 -04:00
agent-quickstart.md ci: verify the Roborazzi goldens, and prove the check can go red (#628) (#629) 2026-08-28 08:01:25 -04:00
audit.md Split test-coverage-audit.md into a verified half and a dated one (#654) (#691) 2026-09-02 23:55:32 -04:00
distribution.md ci: verify FOSS purity against the built APK (#548) (#584) 2026-08-19 20:42:51 -04:00
security-audit-2026-04-07.md Update README and security docs to match shipped behavior (#406 chunk A) (#411) 2026-04-25 18:38:12 -04:00
security-decisions.md Update README and security docs to match shipped behavior (#406 chunk A) (#411) 2026-04-25 18:38:12 -04:00
security.md relay: throttle wrong integration secrets per (IP, subdomain) (#811) (#819) 2026-11-01 06:01:03 -05:00
self-hosting.md Make a partial secret backfill fail loudly instead of exiting 0 (#686) (#693) 2026-09-02 23:23:12 -04:00
test-coverage-audit.md Re-cite workflow.md by line text, not line number, in the coverage audit (#710) 2026-09-03 02:49:47 -04:00
ux-decisions.md Let the user stop the security check entirely (#740) (#741) 2026-09-09 12:03:07 -04:00
workflow.md Annotate the two superseded task records in workflow.md (#687) (#703) 2026-09-03 01:20:25 -04:00