# Product contract

## Published clauses

The clauses below are stable public requirements. The linked sections give their complete interpretation and limits.

- **HTTP-001 — Signed HTTP monitoring.** Public hosted HTTP(S) and keyword checks run in the Cloudflare Worker; private checks use the separately scoped Node runner. They retain the exact immutable assertion bundle and bounded transport evidence, and require authenticated claims and signed results; the control plane must reject contradictory completion. See [Integrity and authority](#integrity-and-authority) and [Network and credentials](#network-and-credentials).
- **EVIDENCE-001 — Immutable scheduled evidence.** Definitions, scheduled slots and every retry attempt retain revision, policy, fixed selected probes, runner identity, timings, status, assertions and error category; retries never erase failures, and observation/state/outbox commit atomically. See [Evidence and state](#evidence-and-state) and [Integrity and authority](#integrity-and-authority).
- **STATE-001 — Fixed coverage and incident hysteresis.** Missing or expired passing coverage is Unknown, partial failure stays visible, independent-vantage agreement and scheduled confirmation determine Failing, and complete fresh scheduled passing evidence plus recovery threshold is required for recovery; manual checks and maintenance never rewrite observations or scheduled policy. See [Evidence and state](#evidence-and-state).
- **AUTHORITY-001 — Server-side scoped authority.** Every private document, read, mutation and export requires server-side operator or scoped runner authority, missing configuration fails closed, browser mutations enforce configured origin, and expired/foreign attempts cannot alter current evidence, and duplicate attempt identities cannot overwrite it; a signature identifies a key holder rather than proving truthful observation. See [Integrity and authority](#integrity-and-authority).
- **NETWORK-001 — Pinned bounded destination execution.** Every public DNS answer and redirect hop is validated fail closed; private-runner numeric sockets preserve Host and TLS identity, hosted checks use Cloudflare’s public Internet fetch transport, unsafe/mixed addresses and HTTPS downgrade are rejected, cross-origin redirects strip request authority, and synthetic grants and protected origin-bound local credential files retain their separate custody limits. See [Network and credentials](#network-and-credentials).
- **HEARTBEAT-001 — Revision-scoped receipt evaluation.** Hashed owner-issued tokens and increasing sequences protect server-time receipts, ingress cannot advance health, and scheduled signed completion must match its exact installation-local claim snapshot; stale, future, paused, foreign-revision or imported receipts cannot establish Healthy. See [Network and credentials](#network-and-credentials).
- **ARCHIVE-001 — Inactive validated restore.** Bounded versioned archives require integrity and semantic validation before transactional restore into empty product data, preserve historical evidence, erasure receipts and the permanent anonymised action audit, create paused current revisions, interrupt leases and quarantine alerts; installation-local sessions, keys, grants, request credential values, heartbeat authority, contact, webhook and Slack URLs, and signing keys are excluded; destination definitions and retained notification histories are portable, and imported evidence cannot establish current health or replay events. The source erasure journal fences copies made before a later erasure when that journal accompanies the restore. A local CLI receiving checkout must run setup first to establish its owner and settings. The owner accepted this installation-authority and endpoint-credential archive policy on 2026-10-13; it is an intentional replacement, not BASE-DATA-001 equivalence. See [Portability and retention](#portability-and-retention) and [Integrity and authority](#integrity-and-authority).
- **IMPORT-001 — Reviewed definition files and inactive imports.** A versioned definition file (2 MiB, 500 monitors, UTF-8 JSON, editor fields only, no request secrets or installation authority) or saved UptimeRobot v3 monitor pages are previewed without writes as create, unchanged, change, refused or unsupported per entry with a plan digest, validated by the editor's own validation; apply refuses a digest that no longer matches and otherwise writes the whole plan in one transaction as new paused monitors or new revisions that keep enabled, maintenance and runner assignments; nothing in a file enables a monitor, issues a heartbeat token, schedules a notification or touches a destination. Each apply is a batch; undo pauses what it created and restores what it changed while their revisions are unchanged, reports the rest and never removes history. Batches travel in archives as restored history without authority. A log export is counted, never stored or turned into evidence. Unmeasured: no real UptimeRobot export and no hosted run. See [Definition files and imports](#definition-files-and-imports).
- **RETENTION-001 — Explicit storage custody.** Product and observation metadata and legacy development sign-in records have no automatic age/count deletion; operator erasure removes captures for the associated operator, and local reset clears the installation. Expired sessions, authentication/public rate-limit state and short-lived login-attempt counters are cleaned up. Expired or confirmed alert-drill links lose their capability material while drill history stays; retired destination secrets are scrubbed only after owner rotation/deletion and in-flight sends finish; a proven-unsent integration-attempt reservation can be removed while its delivery history remains. Other expired operational records, including discovery jobs, change status and remain stored unless an owner erases the associated entity or resets the installation. Raw target response bodies and probe-request secrets are not retained in observations, webhook and Slack endpoint URLs and signing keys remain installation-local, available email addresses travel as destination definitions, view/archive bounds are not deletion or capacity claims, and retention changes must never silently improve the fixed denominator or historical health. See [Portability and retention](#portability-and-retention).
- **ERASURE-001 — Operator-directed entity erasure.** An owner can erase a monitor with its evidence and dependent notification/heartbeat records, a destination with its credential copies, or an operator while another active owner remains. Monitor erasure requires typing the current monitor name. Each completed erasure returns a receipt with entity kind, per-table counts and completion time, and returns it only after the transaction containing the erasure, audit entry and portability fence succeeds. Requests based on stale monitor or destination state cannot re-create erased rows. The last active owner cannot be erased: local installations use `rer reset --yes`, while hosted installations require replacement by redeployment. Uptime has no R2 upload feature, so product erasure has no uploaded attachments to remove. Already downloaded archives, messages already sent to external recipients and requests already in progress remain outside local erasure. See [Erasure and audit](#erasure-and-audit).
- **AUDIT-001 — Durable anonymised audit.** Destructive actions, automated notification attempts and operator authentication-mail sends record actor or feature, time, action, subject kind and an opaque subject reference. Audit history is portable and survives erasure of the subject; erasing an operator anonymises its prior actor references. The audit contains no monitor names, target URLs, email addresses or destination URLs. See [Erasure and audit](#erasure-and-audit) and [Portability and retention](#portability-and-retention).
- **PRESENCE-001 — Contact separate from health.** Only accepted scoped claims and signed results refresh server-time contact, the 120-second freshness threshold never proves process liveness or target health, and restart, unavailable reads and archive omission preserve that distinction. See [Contact, interface and delivery](#contact-interface-and-delivery).
- **INTERFACE-001 — Honest durable operator feedback.** Textual state/freshness/reasons, immutable history, labels, keyboard controls and responsive layout expose persisted evidence; failed reads show Unknown, action errors survive polling, busy reconciliation excludes controls and editing rejects assertion overrides. See [Contact, interface and delivery](#contact-interface-and-delivery).
- **NOTIFY-001 — Authorized installation-wide destinations.** An owner may configure email, signed JSON webhook, or Slack incoming webhook destinations for all monitors in one installation; only scheduled incident transitions generate automatic notifications, and a test send cannot change monitor evidence or incident state. The hosted Worker submits email through the configured Base mail adapter and sends webhooks and Slack without a private runner; a private runner can also claim notification work. Destination credentials are installation-local and are not restored from archives. See [Contact, interface and delivery](#contact-interface-and-delivery) and [Network and credentials](#network-and-credentials).
- **DELIVERY-001 — Bounded notification acknowledgement.** Each event and destination gets one automatic send attempt. A RER relay 429 with Retry-After confirms non-acceptance and queues another submission after that delay. HTTP 500, connection loss, timeout, and unknown outcomes remain uncertain and are not automatically sent again. Accepted means the configured provider or endpoint acknowledged the request; it does not mean a person received, read, or acted on it. See [Contact, interface and delivery](#contact-interface-and-delivery).
- **PUBLIC-001 — Public landing and documentation.** At the app origin, `/` is the unauthenticated Lobby, `/docs` is public documentation, `/setup` is first-run setup, and the private operator console and editor are `/app` and `/app/monitor`; private documents require an owner session. The separate marketing site serves `https://run-uptime.com/` and `https://run-uptime.com/run`. The app Lobby at `/` and the marketing landing at the run-site `/` are distinct surfaces.
- **BOUNDARY-001 — Bounded delivery and operating claims.** Destinations apply to all monitors in the installation, while runners with the same network label remain one vantage. Provider acknowledgement and stored delivery state do not prove inbox receipt or human notification. Teams, Discord, SMS, on-call paging, incident escalation, and public status communication are outside the product; network isolation, browser execution, and accessibility conformance require separate evidence. See [Contact, interface and delivery](#contact-interface-and-delivery).
- **DRILL-001 — Recipient-reported alert drills.** An owner can run an alert drill on one destination: the existing test event with drill wording, sent once through the same path with no automatic resend, plus a one-time confirmation link (random capability, stored only as a bound hash, single use, 24-hour expiry, one drill and one destination). Opening the link shows a page; only a same-origin POST confirms, and unknown, used, expired and foreign links answer 404 alike. The console keeps Submitted, Acknowledged (or Uncertain/Failed with its code) and Recipient confirmed as separate lines, and does not measure whether anyone acted. A confirmed drill is a report by the recipient that one message reached a person at one time. It is never evidence of monitoring, a delivery guarantee or a human response, and it changes no monitor evidence, state or incident. Proof belongs to the destination's current credential: a new address or URL or a rotated secret makes it Never proven, and drill history stays with the installation, so a restored destination is Never proven. See [Alert drills](#alert-drills).
- **STARTUP-001 — Required configuration diagnostics.** A shared configuration table validates required and conditional bindings for the Worker, scheduled entry point, local app, preview, setup, deployment and private runner before each begins work. Missing or malformed required values stop the entry point and name the variable without printing its value; a Worker configuration failure returns 503, including at `/health`. Hosted mode requires the selected Base mail provider and its sender configuration. See [Configuration diagnostics](#configuration-diagnostics).
- **PUBLIC-002 — Declared sessionless paths and rate limits.** Every sessionless route and emitted static path is declared in `seed.json` with its authentication rule and rate-limit class. D1 applies owner-tunable per-client limits in fixed 60-second windows and returns 429 with `Retry-After` after the class limit. A declaration does not bypass route-specific session, runner, heartbeat or development-key checks. Limit values are marked estimated and not load-tested. See [Public paths and rate limits](#public-paths-and-rate-limits) and [Integrity and authority](#integrity-and-authority).
- **FLOW-001 — One isolated browser flow (implementation only, outside the supported public feature set).** A flow monitor is a revision like any other: a plain-language intent, a start URL under the public-target rules, an ordered list of steps from a closed vocabulary (go to a page, click, fill a field with a named runner-local credential, wait for or expect text, a URL or an element) and the required outcomes that must all be observed for a pass. It carries no script, expression or secret, and nothing is added to the bounded HTTP path. A runner that has a browser runs each flow in its own process with a fresh private profile, a sanitized environment, one flow at a time and a process-tree kill at the wall-clock limit; all browser traffic goes through one filtering door that enforces NETWORK-001. A runner without a browser reports that, and the flow is Unknown with that reason. Credentials stay on the runner. Each observation keeps an immutable, sanitized step trace and the digest of the revision it ran, and no screenshot or page body. Exceeding any stated bound is a failure naming the bound. A pass is evidence of that flow only. See [Flows](#flows) and [Network and credentials](#network-and-credentials).

## Declared external data flows and baseline replacements

`seed.json → externals` declares the source-level outbound data categories. Public HTTP(S) monitors and status-only canaries resolve each public target, validate every redirect hop, and send the configured method, path/query, allowed headers and request body to the target. A request credential is sent only to its exact owner-authorized monitor origin; cross-origin redirects strip it, custom headers and body. Ordinary checks read bounded response status, headers and body for assertions without retaining raw bodies in observations. A canary is a status-only `GET` with no credential, custom headers or body and reads zero response-body bytes. DNS queries reveal only the hostnames being resolved plus source network address and lookup timing to the resolver configured for the runner.

Discovery is read-only and runs on the owner's runner. It may follow any validated public redirect from the initial URL; subsequent crawls are bounded to the final/input registrable domains and linked status hosts, subject to robots and request/time/body limits. It may request robots files, permitted pages, JSON/spec documents, conventional API hosts and status hosts. Bounded response content is parsed in runner memory. Only validated definitions, hostnames, URLs, short notes and request/timing summaries return to the Worker; raw HTML, response headers and page bodies do not. The Worker stores only the bounded derived result. No supplied HTML is rendered as console HTML.

Browser flows send ordinary browser requests to owner-declared top-level origins and any public subresource or WebSocket host reached by a page; redirects and subresources pass the same destination-filtering proxy, which blocks private, loopback, link-local and metadata addresses. The destinations receive request paths, browser headers, source address and timing, and cookies that a destination itself sets. A named runner-local credential can be filled into a form only while the page is at its exact configured origin, so that destination may receive that value as part of its form request. The isolated runner browser processes page content locally; only the sanitized step trace returns through the control plane, not screenshots or page bodies.

The private runner sends signed enrolment, capabilities, claims, contact timing, signed attempt evidence and derived discovery results to the owner-configured control-plane origin. It receives assigned monitor/flow definitions and discovery jobs from that control plane. A notification claim can return the selected endpoint URL, signing credential and event content to the runner, which sends the one authorized webhook or Slack request and returns its bounded delivery outcome. The hosted Worker submits queued email through the selected Base mail adapter and sends webhooks and Slack itself. Owner-console requests prompt due checks and sends; Cron continues background work after activation. A private runner may also claim delivery work, but never receives mail-provider credentials. The selected mail provider receives its API-key authorization (and Cloudflare account identity when selected), sender and recipient addresses, message subject and text/HTML body, sign-in links, incident/test/watch content and drill confirmation links. SendGrid receives the delivery ID in custom arguments, and Resend receives it as the idempotency header. Provider acknowledgement does not establish recipient acceptance. Optional runner-local watch endpoints receive only unreachable/recovery state and timestamps. An optional owner-configured heartbeat URL receives an empty HTTPS request whose capability path, source address and timing are visible to that service. The shared local lifecycle also makes a readiness `GET` to the selected application origin (normally local loopback, possibly an owner-selected remote origin); the CLI follows HTTP redirects, sends no configured authorization header or body, and reads the health status/JSON value.

The optional `IDENTITY_PROVIDER` setup declaration is not a working sign-in feature. The completed local probe passes the real public contract check, omitted-provider setup, native email-link sign-in and `/setup` handoff, native session revocation, and repeat setup with exact settings, owner and dependency-row preservation. With a selected provider, the real CLI reads dependency metadata from a local issuer edge, then setup fails at the unmounted application dependency-approval route (404) before creating an owner; no provider link request is made. A separate native bootstrap succeeds, but dependency discovery/linking and OIDC start/callback routes remain 404 with no durable link or identity records. That 404 is not linked-provider revocation proof. The current Worker does not exchange authorization codes, user tokens or JWKS. The default is a separate scoped provider-lifecycle adoption round; this proof establishes no linked-provider readiness. Likewise, the private management editor is an explicit owner-configured integration: when the exact management origin and installation identity are supplied, an authenticated owner page creates its iframe while hidden and sends installation, parent-origin and surface context, followed by the page path after handshake. Visible page text and element descriptors are sent only after the owner opens the editor and enables context sharing. Uptime provides no public embeddable widget.

For **BASE-INPUT-002**, Uptime replaces arbitrary-HTML sanitization with no owner-supplied HTML field and derived-only parsing of fetched public target content. For **BASE-INPUT-003**, Uptime has no R2 binding or product attachment-upload route; bounded definitions and archives are structured data, not attachments. For **BASE-PUBLIC-002**, Uptime has no public embeddable surface; only the separately configured private management editor origin is used. For **BASE-DATA-005**, the replacement is a standalone native product with no operational dependency on the marketplace or author, plus an optional owner-configured private management editor. That opted-in editor may contact the author when the configured management origin belongs to the author; an absolute author-zero-egress claim therefore does not apply to that configuration. The native product must remain usable without it. No telemetry, licence check or kill switch is authorized by this exception. These are explicit scope replacements, not untested baseline passes. A measured emitted-runtime probe with optional integrations absent denied outside Worker/browser traffic and the exercised runner HTTP(S) transport and passed native sign-in, actual local-edge monitoring failure/recovery, history and export, with zero outside browser requests at 1440/390. Hostile text in monitors, destinations and structured definition imports stayed literal without planted requests or script execution; unknown uploads were refused without durable effects. This proves the tested native flows operate independently. The source inventory and scoped trace do not establish complete optional-provider or hosted/deployment egress.

## C3 — Stored evidence and independent failure agreement

A slot outcome and scheduled confirmation/recovery streaks are pure projections of stored attempts, recalculated on each arrival in chronological slot order. A derived checkpoint folds only expired acceptance windows and keeps exact, unclamped counters; complete slots still accepting late attempts remain mutable. Reads re-evaluate current freshness without advancing the checkpoint counters. Failing requires terminal target failures from two distinct runner network labels, or one failing vantage with its passing canary in the same slot. A failed canary counts its network absent; its failure remains Degraded. Missing/expired evidence is Unknown; recovery requires complete fresh passing coverage. Same-label runners count once. New enabled production monitor settings refuse configurations incapable of reaching Failing: a public monitor on one network label with no external check URL of its own takes the installation's External check URL (an owner-only setting on Runner / archive, seeded with `https://example.org/`) into its saved revision, so it is refused only when the setting is absent. An external check on the monitored site itself (the same hostname, ignoring case and a leading `www.`) is refused whether it is the default or the owner's own, because it would fail whenever the monitor does and a failure could never be confirmed. Revisions are immutable, so changing the setting applies to monitors saved afterwards and never rewrites a saved one. The setting is installation-local and does not travel in archives; the URL a revision carries does. Assignment-free historical revisions keep their saved terminal-probe failure quorum until the owner saves a new revision; the overview and count-only owner notices flag the need for a second independent network or an external check URL. `tests/resilience/attempts.mjs` proves these flows through signed Worker routes and real probes at local network edges.

## C4 — Per-runner assigned attempts

Each runner has an independently revocable key and a declared network label. A revision assigns each probe to a runner. The immutable attempt identity is slot/probe/attempt number; only that assigned, unrevoked runner can submit it inside the acceptance window. The first accepted copy wins. Results have no slot-wide lease owner and may contain any assigned subset. Stored evidence, recalculated state and incident events commit atomically under a generation guard. Migration preserves the existing runner as the first row. C5's pinned resolution remains unchanged. `tests/resilience/attempts.mjs` covers migration, signatures, assignments, duplicates, expiry, order and revocation through public routes.


## Evidence and state

Every scheduled slot and attempt retains its monitor revision, policy, selected probes, runner version, start/completion, status, assertion results and error category. Retries are attempts inside a slot and never erase an earlier failure or increment scheduled confirmation/recovery streaks. Raw target response bodies and probe-request secrets are not retained in observations; notification destination credentials remain installation-local and are omitted from portable archives.

The 24-hour and 7-day history coverage includes complete scheduled results across revisions, evaluated with each immutable revision’s settings and bounded by its next revision. Maintenance and paused periods are shown separately from observed and Unknown time; historical results cannot establish current-revision health. Last check retains the latest completed scheduled observation across revisions.

Target evidence, coverage, and notification delivery are independent. Healthy requires fresh, complete selected-probe coverage and the configured recovery threshold. Incomplete passing coverage is Unknown. A fresh failure without independent-vantage agreement is Degraded; meeting the C3 agreement and confirmation threshold is Failing even when coverage is incomplete. Expired evidence becomes Unknown without silently resolving an open incident. Recovery requires complete fresh passing scheduled slots. Runner errors and absent observations remain distinct from target failure. Scheduled incident transitions and their destination jobs are committed with the corresponding observation and state change; a test send is separate from that history.

Maintenance suppresses event emission while preserving observations and target state. After maintenance expires, a real completed slot may open a still-confirmed failure. Recovery requires a previously open trigger, preventing orphan recoveries. Pending means stored locally. Manual checks do not change scheduled cadence, confirmation or recovery.

## Configuration diagnostics

The shared configuration table describes each binding, the runtime that consumes it, when it is required, and its validation rule. The Worker checks it before serving any path and before scheduled maintenance runs. Setup validates its generated or saved settings before starting its temporary runtime; the local app, preview, deployment and private runner validate their own configuration before serving or starting operational work. Invalid or missing values produce plain diagnostics naming the binding but never its value. The Worker's `/health` response is 503 until required configuration is valid. Hosted setup requires the selected Base mail provider credentials and sender address so email is not silently unavailable.

The applicable `/health` dependencies are D1 (including public admission, schema readiness and credential-custody storage) and the emitted `/app.js` through `ASSETS`; this product has no R2 or queue binding. A 900 ms outer response deadline bounds those checks and returns 503 on unavailable dependencies. D1 has no cancellation API, so deadline expiry bounds the response rather than cancelling the underlying operation. The retained original local emitted-Worker health log records 15 total cases: healthy 200 and 14 dependency faults, all 503 within 906 ms, each followed by healthy recovery. Twenty sequential healthy samples have p95 6 ms and maximum 7 ms. These measurements establish neither hosted latency nor load capacity, runner operation, target health or provider acceptance. Existing public throttling still returns 429.

## Public paths and rate limits

`seed.json → deploy.publicPaths` declares sessionless documents, authentication routes, assets, health, credentialed runner and heartbeat APIs, owner-editor configuration, redirects and local-only development endpoints. The server still performs each route's normal authentication check after the rate-limit admission. `seed.json → limits.publicRequestsPerMinutePerIp` sets an owner-tunable maximum for each path class in a 60-second D1 window. Hosted requests use a keyed hash of the Cloudflare client-IP header; missing or malformed values share an unknown-client bucket. Local requests share a loopback bucket regardless of forwarded headers. Excess requests receive 429 and `Retry-After`; unavailable counter storage returns 503. Only expired public-rate counters are removed automatically; those counters are short-lived operational state and not product records. Limits are marked estimated and not load-tested, and do not establish capacity.

## Integrity and authority

Each Node runner signs purpose-separated enrolment and results with its own scoped key. Identity, declared network label, revocation and credentials stay installation-local. Legacy key-only clients resolve to the migrated first runner; they have no authority over other runners' probes. See C4 above.

Slot identity is monitor/revision/due time; attempt identity includes slot/probe/attempt. Manual requests have distinct identities. The acceptance window is bounded; expired, foreign or contradictory attempts are rejected and the first accepted copy wins. Edited or paused inflight work remains historical evidence and cannot change current state or events. Missed cadence is counted without fabricated catchup results. A scheduled check is due no sooner than one interval after the monitor's previous scheduled check was claimed and after its first attempt reported starting; after an edit, the shorter of the old and new intervals applies, so a late check moves later due times back rather than running checks closer together; equal runner and Worker clocks make the reported-start floor exact. Backward clock movement waits for due time; future evidence cannot establish health.

Observation, state and outbox writes share one durable transaction. Migration history is permanent. Completion verifies the immutable definition, fixed probe denominator, exact assertion bundle, outcome, clock bounds and transport limits. Live latency validation uses the larger retained elapsed measure. Historical archive compatibility does not relax live validation.

Every private document, mutation, observation read and export requires server-side operator or scoped runner authority. Missing configuration fails closed. Browser mutations bind the configured origin and reject cross-site requests. Loopback is not authentication. Shared Base operator-auth owns operator credentials and sessions; product policy must not silently grant additional roles. Signed reporting identifies the holder of the runner key; it does not prove an uncompromised runner.

## Network and credentials

Private-runner public HTTP(S) checks validate every A/AAAA answer and redirect hop, reject unsafe or mixed address sets, pin numeric sockets and retain original Host, SNI and certificate hostname verification. Proxy environments are bypassed. HTTPS downgrade is rejected; an origin change strips credentials, custom headers, request body and mutating method. DNS, redirects, body and retries have bounded execution. Responses are limited to 64 KiB; compressed responses are rejected. An external check asserts status 200 only, so it stops at the response headers and neither reads nor keeps the body, however large the page; every other guarantee here applies to it unchanged. Independent network isolation remains an operating requirement. Hosted HTTP(S) and keyword monitors without runner-local credentials default to the Cloudflare Worker. Owner-console requests prompt due work; its one-minute Cron schedule continues background work, but initial Cloudflare activation can take up to about two hours. The Worker validates all public DNS answers and every redirect and uses Cloudflare’s public Internet fetch transport with certificate verification, no private-network binding, no cached probe response and the same time, body and assertion bounds. Numeric socket pinning is the private runner’s transport guarantee. The Worker is one network vantage; it uses the same configured canary and scheduled evidence rules and cannot supply independent regional agreement. Other checks require an assigned private runner.

Synthetic checks retain a separate live process/custody grant, exact path/port identity, expiry, GET/HEAD-only and no-redirect boundary. Grant authority is rechecked on every attempt.

Request headers, body and bearer/basic credentials are protected runner-local regular files with exact monitor-ID and origin binding. Credential authority comes from the authenticated task envelope while its assigned probe and acceptance window are valid; another monitor on the same runner cannot use the same reference to obtain the credential. Setup runs numbered local-file migration 0001: valid unbound files used by exactly one existing monitor bind automatically, counting paused monitors; shared and unused files remain unbound with explicit owner instructions. Scheduled checks record bounded non-secret binding diagnostics and never bind files. Missing or invalid references yield runner errors without falling back to unauthenticated requests. Raw secrets and response bodies never enter observations, alerts or archives. Portable historical references are not receiving-installation execution authority.

Heartbeat tokens are owner-issued, hashed at rest, scoped to an immutable revision and rotated on issuance. Server receipt time and strictly increasing sequences prevent replay. Ingress alone cannot change state. A scheduled slot freezes an installation-local receipt snapshot; signed completion must match it. Missing, stale, future, paused, revision-mismatched or imported receipts cannot establish Healthy.

## Flows

Browser-flow code remains outside the supported public feature set. The following describes its implementation obligations, not a public feature promise.

A flow is validated by the same function in the Worker, the definitions engine and the runner. Steps are `goto`, `click`, `fill` (a `credentialRef`, never a value), `waitFor` and `expect`; a target is exactly one of a plain CSS selector, text, label, or a role with its name. Required outcomes have an id and an expectation, and every one must be observed after the last step. Unknown step kinds and fields, scripts, other selector engines, secrets, intervals under 5 minutes, more than 20 steps or 8 outcomes, and more than 5 flows in an installation are refused with the entry named. Bounds per run: 60 seconds of wall time, 8 top-level pages, 5 MiB of response bytes, an 8 KiB trace; exceeding one fails the run and names it.

The runner serves a flow only through `runners/src/flow/`: its own process, a fresh 0700 profile deleted afterwards, an environment holding no inherited credential, proxy setting or Node hook, and the named credentials passed over a pipe. Chromium's only route to the network is a filtering proxy inside the runner; it resolves each destination itself, connects to the address it checked, refuses private, loopback, link-local and metadata addresses for pages, redirects, subresources and WebSockets, and reaches the local fixture only through the synthetic grant. A page may load top-level pages only on the origins the flow names and may not open a second window.

A flow runs a probe like any monitor, so incidents need the same vantage agreement. Failures keep distinct reasons (expected text or element missing, no element for a click, an error status, the wall limit, a forbidden address, a second origin, a missing outcome); a runner-side problem (no browser, a missing credential) is a runner error and the flow is Unknown. An observation's `flow.digest` must equal the digest of the flow in its slot's revision. When the revision now running requires less than the revision that was current when the open incident began, a pass recovers that incident only with the changed-definition label in the state, the recovery event and the console, and the revision history shows when the required outcomes shrank. Credentials are runner-local files bound to an exact origin and appear in no table, archive, export, log, trace or audit record. The flow tables are the existing monitor, revision, slot and observation tables; no table was added.

## Definition files and imports

One engine serves the owner console route (`/api/definitions/*`, origin-bound, owner session), the local command `node scripts/definitions.mjs` and the read-only definitions export. Importers only split a source into entries and map each to a file entry or an unsupported/refused reason; preview, apply and undo are source-independent. The UptimeRobot adapter maps HTTP(S) and keyword-must-contain monitors, a single expected status (ranges become exactly 200, listed), method, timeout (bounded to 10 seconds, listed), interval (5 seconds to one day, otherwise refused with its value) and heartbeats (a new owner-issued token is required; the old URL is listed for repointing). It lists, and never silently drops, other monitor types, keyword-must-not-contain, request authentication, headers and bodies (a local credential reference is created instead), regions, alert contacts, status pages, maintenance windows, tags and groups. Imported monitors check from probe local-a and need a second network or a canary before enabling. A recognised log export reports its row count and provenance; no row becomes a slot, observation, state or incident, so imported history cannot satisfy quorum, recovery or the 24-hour and 7-day figures. Migration 0026 adds `definition_batches` and `definition_batch_entries`; monitor erasure removes a monitor's batch entries and operator erasure anonymises batch actors. `tests/definitions-import.mjs` proves the Worker door and `tests/archive-roundtrip.mjs` the archive behaviour; the mapping was tested with synthetic files only.

## Alert drills

A drill exists to check, one destination at a time, that an alert reaches somebody who can act. **Run alert drill** queues the test event with drill wording (`eventType` `uptime.notification.drill`, `kind` `test`, `incidentKey` null) for that destination's current credential. Disabled destinations can be drilled, as they can be tested. The one automatic attempt and the uncertain-never-resent rule are DELIVERY-001's. The message names who started the drill and when, and carries a link to `/alert-drill` holding a random 256-bit capability. Email has it as a text line and an HTML link, Slack as a message line, and the signed JSON webhook as an extra top-level `drill` object (`startedBy`, `startedAt`, `expiresAt`, `confirmUrl`) on drill events only. Incident events, their payload shape and Lamp-compatible signing are unchanged.

D1 stores the capability's SHA-256, bound to the drill, the destination and its credential. A copy sealed under the installation key lives only until the single send is over, and the runner reads it only while it holds that delivery's claim. The stored event, the stored webhook/Slack envelope, the archive and the console reads never contain the link. Opening the link (GET or HEAD) shows a page naming only the destination kind and the drill time. Only a same-origin form POST confirms, compared in constant time and accepted once within 24 hours. Unknown, used, expired and foreign links answer the ordinary 404 page, and the path is declared in `seed.json` under its own rate-limit class. The recipient needs no sign-in.

The console shows, per destination, "Last proven" (the latest confirmed drill for the current credential), "Not proven in 30 days" or "Never proven" in words, beside the latest drill's three lines:

- **Submitted** is when Uptime started the one send. Before then it says the drill is queued, or why nothing was sent.
- **Acknowledged** is when the provider or endpoint answered and with what code, or Uncertain or Failed with the code. Acknowledgement is not inbox receipt.
- **Recipient confirmed** shows the time someone used the link, reported by the recipient. Until then it is pending, and after 24 hours it reads "not confirmed within 24 hours" with nothing sent again.

Whether anyone acted is not measured. The first-run page lists any destinations already present with the same proof words. Drill history (`alert_drills`) is installation-local: archives carry the drill's delivery history like any test send, but never the drill rows, so restored destinations start Never proven. Erasing a destination erases its drills; erasing an operator removes the operator reference from the drills they started. Unmeasured: no hosted drill to a real inbox or channel has been run (see ACCEPTANCE.md).

## Portability and retention

Versioned archives include definitions, immutable observations, state, product outbox events, notification history for source records that remain, erasure receipts, the permanent anonymised action audit, source identity and erasure journal, and an integrity digest. Legacy development sign-in records are installation-local, not archive data, and remain until their associated operator is erased or local reset; no captured-mail viewer or new sign-in capture is exposed. Restore validates bounded UTF8 bytes, schema, version, semantics and hash before transaction and requires empty product data. Existing receiving-owner, runner-registration and idle-contact records do not make product data nonempty and remain unchanged by restore. Imported monitors gain new paused current revisions; leases are interrupted and alerts quarantined. Derived attempt checkpoints, request credential values, heartbeat authority, sessions, keys, grants, contact, webhook and Slack URLs, and signing keys are installation-local and are not imported; destination definitions and retained notification histories are portable. A local CLI receiving checkout must first run `rer setup --owner-email EMAIL --non-interactive` to establish its owner and settings; setup leaves product data empty for import. No imported event or receipt automatically replays. A digest checks changed bytes, not trustworthy provenance. To restore a pre-erasure archive after an erasure, export the later source journal from Runner / Archive and provide it to restore; local CLI import accepts its absolute path in `UPTIME_LATER_JOURNAL`. By itself, an old offline archive cannot reveal later erasure when restored into a fresh installation that has never received that journal. This is a limit of Base's offline transfer model.

Observation metadata, monitor definitions, destination and delivery history, audit history, and legacy development sign-in records have no automatic age/count deletion. The maintenance sweep deletes expired operator sessions, old owner/auth rate-limit windows, expired public rate-limit rows and old login-attempt counters. Short-lived authorization rows can also expire logically without being deleted; expired discovery jobs are marked expired and retained. Drill expiry/confirmation clears only the one-time capability ciphertext/hash while retaining the drill row. Owner rotation/deletion retires endpoint credentials by scrubbing their secret material after active sends finish while keeping the credential/history row. The notification bridge removes an `integration_attempts` row only when it proves that the exact attempt never reached the send boundary; its delivery reservation and job remain. A setup migration removes stale, unclaimed dependency-approval state. Explicit owner erasure and local reset are separate destructive actions. The inspected local runtime scan compared all 76 migrated tables in a populated installation; 36 held rows before maintenance. Only aged `oa_sessions`, `oa_limits`, `login_attempts` and `public_rate_limits` rows decreased, and all other preexisting row identities survived the measured cron/lazy-expiry paths and Worker restart. This scan covers its listed execution classes, not every active automatic cleanup transition or optional provider integration. View limits (100 history slots and 200 outbox entries), monitor limits and archive limits are not deletion rules or measured storage capacity. Storage growth remains an operator responsibility. Deletion must never silently improve the configured denominator or historical health.

### Erasure and audit

Monitor erasure removes its revisions, slots, observations, derived attempt checkpoints, heartbeat claims and authority, incident events, related notification jobs and deliveries, and archive-history copies. Destination erasure removes the destination, all credential versions, dependent notification jobs, delivery attempts and outcomes, replay entries and matching archive-history copies. Operator erasure removes that operator's sessions, tokens, invitations, legacy development sign-in credentials and messages. For the original owner, the bootstrap claim keeps its singleton and claim time but replaces the operator reference with an unrelated anonymous UUID. Its receipt counts that row as anonymised, and the source journal fences its original primary key. The replacement commits in the same guarded batch as account deletion; setup remains claimed and receiving-installation authority remains local. The audit remains separate from the erased entity: its subject reference is opaque, and actor references for an erased operator become anonymised. Erasure writes a durable receipt with per-table counts and completion time in the same D1 batch as its audit record, deletion and Base portability erasure-fence statements.

The audit includes destructive actions and automated notification attempts, including test sends, plus operator authentication-mail provider attempts and legacy development capture history. The mail sender records the feature audit before attempting the provider request. Actor and subject fields are identifiers or feature names, never user-supplied names, emails, URLs or secrets. The audit travels in product archives. A later erasure journal makes restore refuse old audit actor references for an erased operator. Older offline archive bytes and external messages already sent cannot be changed by a later local erasure; a request already in progress may still reach its provider. Uptime does not use R2 or accept uploads, so there are no uploaded attachments to remove.

## Contact, interface and delivery

Authenticated runner contact records control-plane receipt time for accepted scoped claims, including idle claims, and accepted signed results. Owner reads, health checks and rejected requests do not refresh it. Fresh means within 120 seconds; future or unverifiable time is stale. Contact does not prove process liveness or target success. Restart retains it, archives omit it, and failed overview reads label contact unavailable with any prior timestamp clearly historical.

The interface exposes textual state, freshness, reasons, immutable revisions and manual provenance, with labels, keyboard controls and responsive layout. Failed reads show Unknown and last successful refresh. Action errors persist across polling; busy controls stay excluded through reconciliation. Editing must preserve configured checks; duplicated reserved typed assertion keys are rejected rather than silently overriding criteria. No fabricated uptime figure or color-only state is acceptable.

An enabled email, signed JSON webhook, or Slack incoming webhook destination applies to every monitor in that installation. The hosted Worker submits email through the configured Base mail adapter and sends webhooks and Slack without a private runner; a private runner can also claim notification work. The one-time test action queues a distinct test event and does not change monitor state. Only scheduled incident transitions create trigger and recovery notifications; maintenance suppresses these events, and manual checks do not create them. Portable archives include destination kind, name, enabled setting and available email address, but exclude webhook and Slack URLs, signing keys and other credentials. Restored destinations are disabled until the receiving owner reauthorizes and enables them.

Each event/destination pair has one automatic send attempt; a RER relay 429 with Retry-After queues an unaccepted message until that delay expires. HTTP 500, timeouts, connection loss, and unclassified responses remain uncertain without automatic resend. A successful provider or endpoint response means only that the request was acknowledged. It is not proof of inbox delivery, a person reading it, or an operational response. Authentication mail remains separate from incident email. A single runner is one network vantage; logical probe names do not establish geographic independence. Teams, Discord, SMS, on-call paging, incident escalation, and public status communication are outside scope. Browser execution, production network placement, recipient acceptance, and accessibility conformance require separate validation.

Notification endpoints and webhook signing keys are authenticated encrypted
at rest under installation-local Worker-secret custody. Rotation cancels
queued work using the old credential. Once no send is in progress, retirement
scrubs endpoint, signing key and ciphertext and sets `retired_at`; the credential
row identity and committed delivery history remain. A genuinely sending obsolete
version retains its material only until its outcome. Destination erasure removes
all credential versions and dependent local delivery history. Slack uses its
capability URL alone and has no Uptime signing key.
The key-bearing upgrade encrypts retained credentials, cancels obsolete queued
work and scrubs unused or terminal versions. A genuinely sending obsolete
version remains available only until its outcome. Individual undecryptable
credentials fail closed without disabling monitoring or sign-in; the owner can
re-enter or delete them. Email addresses are readable by the authenticated
owner; URL credentials and signing keys never appear in destination lists.
Destination comparison hashes use purpose-derived HMAC custody. An ahead
runner leaves queued work available to a correctly timed runner. Only an owned,
fresh and durably unstarted claim can return to the queue; its reservation
identity is retained separately from network attempts. Started, expired and
uncertain attempts are never automatically replayed.

Durable route-visible outcomes have been measured for exhausted HTTP, keyword
and heartbeat retries, abandoned leases, discovery outcomes, notification
failures/uncertainty and drill expiry. The flow retention run cancelled before
browser launch; it does not establish browser-execution acceptance. The wider
BASE-OPS-004 all-execution-classes guarantee remains partial: Worker
`monitoring_watch` stores only its latest per-window outcome and has no
lease-expiry reconciliation, external-heartbeat watch replaces its prior
outcome, runner-local outage/recovery state is in memory, and authentication
mail audit records a provider attempt without a durable provider outcome or
crash reconciliation.

### Complete product archive

Non-secret HTTP/browser credential references and monitor IDs survive archive restore; credential files and local runner authority never travel. A definitions import creates fresh IDs and receives no authority over existing files.

The complete product archive includes notification destination kind, name, enabled setting and available email address, incident/outbox events, notification jobs, delivery envelopes, attempts, resolutions and received replay history for records still in the source. Email addresses are definitions decrypted only through the installation secret-bound custody implementation; unavailable active email definitions cause explicit export refusal. Legacy soft-deleted destination rows that remain in a source database may appear with their remaining definition fields. Current destination erasure removes the row and its dependent notification history before later exports; its receipt and audit remain. Webhook and Slack URLs, signing keys, credential versions, claims, sessions, runner/heartbeat authority, synthetic grants and request credential values are excluded. Schema 2 has an explicit versioned product-table extension; prior six-table schema 2 and legacy schema 1 remain accepted.

Restore preserves sanitized original state and notification history in a flat content-addressed archive-history ledger while live definitions are paused/disabled, queued or running slots interrupted, outbox quarantined and jobs prevented from dispatch. No imported event is replayed. Exports refuse named 10,000-row per-table, 100-monitor and 2 MiB bounds rather than truncate. Local `rer export --output FILE` and `rer import --input FILE --yes` use the same archive implementation as the console with private regular archive files and installation settings. Before local import, setup must establish the receiving owner and private settings; product and history tables must remain empty. Empty-product validation covers product/history tables, including orphan histories, while preserving owner identity, migration history and exact private settings. The explicit schema inventory classifies authentication identities, authorization audits, development sign-in mail and dispatch reservations as installation-local authority. Unknown tables or foreign integration namespaces cause named export refusal rather than omission. The replacement proof is `tests/archive-roundtrip.mjs`, which checks complete portable records and byte-preserved originals against each explicit inactive-restore transformation. The BASE-DATA-001 installation-authority and endpoint-credential exclusion is the owner-accepted policy dated 2026-10-13; available email addresses remain portable destination definitions. This is the recorded replacement for this edition, not full BASE-DATA-001 equivalence.


## Command contract

Uptime uses Base's portable rer CLI with Node 22.20.0 and pnpm 10.12.4. Setup establishes the first owner, uses configured email-link sign-in and local dev identity, preserves prior owner/data/settings and stops its temporary app. Dev provides real Vite assets/HMR. The canonical local state is `.wrangler/state`; unexpected state placement requires explicit recovery. Reset is confirmed and contained; builds exclude and restore private settings. CLI archives use Base integrity and named payloads around the complete Uptime archive, including the audit and source erasure journal, and atomic inactive restore. The console archive format and historical restore policy remain supported. Remote CLI transfer is unavailable. Deployment and hosted acceptance remain separate.

Signed Assisted admission uses `/auth/owner-claim`, trusted installation bindings
and hidden setup credentials, followed by the same native owner session. A
second valid signature cannot replace a claimed owner. Native bootstrap remains supported.
`/api/editor/config` is an owner-only private no-store route; the editor receives
no setup, session or provider secrets. See `scripts/delivery/README.md`.

## Independent monitoring watches

See [resilient monitoring](docs/resilient-monitoring.md) for runner-local unreachable/recovery notices, Worker stale-contact email, optional external heartbeat pings and weekly all-healthy digest. Tests exercise stopped local Workers and heartbeat silence without live providers. These watches do not claim standby execution or human recipient acceptance.
