openvibers/openvibe.watch CHANGELOG.md
CHANGELOG.md
| 1 | # Changelog |
| 2 | |
| 3 | ## 2026-11-09 — a routine is a watch with a cron (Actor's triggers) |
| 4 | |
| 5 | - **A schedule watch**: `POST /api/v1/watches/:id/schedule` saves a five-field cron and timezone (migration |
| 6 | `0021_actor_schedule`). The scheduler fires it like a cadence; `check.run` records the occurrence as a |
| 7 | `condition_met` run with carrier `schedule` and queues the watch's actions without fetching anything. The floor is 15 |
| 8 | minutes, as it is for a cadence. The source stays a valid `watch.watch@1` kind (the subscription is created paused, a |
| 9 | webhook source is refused deliveries, and the routine never polls a page), so the released watch schema is unchanged. |
| 10 | - **A watch that fails five checks in a row pauses itself** and says so (one `agent.step` ended step); resuming clears |
| 11 | the streak and arms it again. A routine's action then reaches Actor with `watch_id` and the Actor `trigger_id` from |
| 12 | the watch's `actor_trigger` label, so the run links back to its card. |
| 13 | |
| 14 | What shipped on OpenVibe.Watch, newest first. The plan is T18 ("Watch complete"); each entry names |
| 15 | the step it landed. |
| 16 | |
| 17 | ## 2026-11-06 — the chat apps' checks come from the SDK (agent plan A4) |
| 18 | |
| 19 | - Slack's v0 request signing, Discord's Ed25519 interaction signature and Telegram's secret token |
| 20 | are checked by `openvibe-sdk/chat-apps` (`verifySlack`, `verifyDiscord`, `verifyTelegram`), the |
| 21 | same code the chat bots use, instead of Watch's own copies (`server/webhook-sources.js`). The |
| 22 | behaviour is unchanged: the same five-minute window, the same exact raw bytes, the same |
| 23 | constant-time comparison, and a provider Watch does not know still checks nothing. |
| 24 | - Pins openvibe-sdk v0.92.0 (was v0.82.0). |
| 25 | |
| 26 | ## 2026-11-03 — email in (agent plan A3) |
| 27 | |
| 28 | - A webhook watch can have an email address of its own (`in-…@openvibe.watch`): mail sent there runs its check on the |
| 29 | sender, subject and text. Cloudflare Email Routing hands each message to the mail Worker (`edge/mail-worker.mjs`), |
| 30 | which posts it to `POST /hooks/email`, signed; an unknown address or a paused watch bounces the mail |
| 31 | (`server/webhook-sources.js`, `server/mail-parse.js`, migration `0016_webhook_email_address`). |
| 32 | |
| 33 | ## 2026-11-01 — the condition in words, a quiet test before arming, and the evidence in every alert |
| 34 | |
| 35 | - The watch list and each watch page say the watch's structured condition in one sentence — what it reads, where, |
| 36 | and what makes it fire — instead of the condition operator alone (`server/render/watch.js`, `server/http/pages.js`). |
| 37 | - A polling watch made on the site is saved paused and read once by a quiet test check before it arms. The watch page |
| 38 | shows the value that check found (or the state it recorded and why, when it found none), nothing is notified and no |
| 39 | action is queued, and the person arms it when ready. The first arm is stored in `watches.armed_at` |
| 40 | (migration `0014_armed_at.sql`, `server/check.js`, `server/http/pages.js`). |
| 41 | - Every alert now carries its evidence: a notification's message and `rich_content`, a webhook body, an Actor task's |
| 42 | context and a run-job's firing argument state the value before and after, the source address and the time the value |
| 43 | was fetched. A part the watch did not store is left out (`server/actions.js`). The watch page's alert history shows |
| 44 | the same evidence (`server/http/pages.js`). |
| 45 | - Tests: `test/site.test.js` (the quiet test, the failed test, arming, the condition in words, the arm badge), |
| 46 | `test/actions.test.js` (the notification and webhook evidence, the omitted line, the page history). |
| 47 | |
| 48 | ## 2026-10-31 — a person's Network session token on the API |
| 49 | |
| 50 | - The watch API accepts a signed-in person's Network session token (`aud: openvibe.network`) as a Bearer, the same |
| 51 | token the site's SSO verifies, as well as a person/agent token minted for `openvibe.watch`. A first-party service |
| 52 | such as Shoes can therefore create a watch with the person's own token instead of a service token. The watch is |
| 53 | owned by the token's canonical `usr_…`/`agt_…` `subject_id` (a Network session token's `sub` is the numeric |
| 54 | account id), so project delegation and the person-only actions keep working. `server/auth.js`, `test/api.test.js`. |
| 55 | |
| 56 | ## 2026-10-13 — check-operation usage readings (plan T5 step 14, Watch slice) |
| 57 | |
| 58 | - A completed check queues one `platform.usage-sample@1` reading (metric `check-operation`, quantity 1, unit `check`, |
| 59 | operation and provider the carrier, subject the owner, project only when the watch names one, cost the run's |
| 60 | `cost_usd`) in the transaction that records its run, keyed `watch:check:<run id>`; a failed check is not billed. |
| 61 | - `server/billing.js` relays them to OpenVibe.Billing (`POST /api/v1/usage`, `billing.usage.record`) with the |
| 62 | `openvibe-sdk/usage` reporter; 400/422 rejects a reading, 429/5xx keeps it pending. `/api/ready` reports its counts. |
| 63 | - Off by default (`WATCH_BILLING_INTERVAL_MS=0`). Migration `0008_billing_readings.sql`, `test/billing.test.js`. |
| 64 | |
| 65 | ## 2026-10-08 — account export and deletion (ADR-033) |
| 66 | |
| 67 | - `POST /internal/events` (loopback, `WATCH_EVENTS_SECRET`) answers network.account.export_requested and |
| 68 | network.account.deleted through `server/account-data.js` over `openvibe-sdk/account-data` (SDK v0.37.0, was v0.36.0). |
| 69 | - **Export:** the person's watches. |
| 70 | - **Deletion:** their watches with every check run, observation and endpoint state under them, and their sessions. |
| 71 | - The two subscriptions are created at boot. Migration `0003_account_data.sql`, nginx `location /internal/ { return 404; }`, |
| 72 | `test/account-data.test.js`. |
| 73 | |
| 74 | ## 2026-10-08 — the public site (T18 step 8) |
| 75 | |
| 76 | - **Sign in with OpenVibe** at `/auth/login`: OAuth 2 authorization code with PKCE (S256) as the |
| 77 | Network client `watch`, a session in Watch's own database (only the hash of the cookie token is |
| 78 | stored), and the signed-in person's `usr_…` subject as the owner. |
| 79 | - **A site for watches**: the home page explains Watch and starts one of the four kinds; `/watches` |
| 80 | lists yours; `/watches/:id` states a watch in plain words with its observation history and every |
| 81 | check run; pause, resume, delete and check-now are plain forms. Every page works without |
| 82 | JavaScript. |
| 83 | - **One create form, four starting points** (`/watches/new?template=page|price|feed|json`): a page |
| 84 | that changes, a price below a value, a new item in a feed, a JSON value — the server picks the |
| 85 | fields the kind needs. |
| 86 | - **Honest by construction**: the kinds that arrive later (event, webhook, Run, Node) and an AI |
| 87 | reading are not offered, because Watch will not pretend to check what it cannot. Notifications go |
| 88 | through OpenVibe.Network exactly as before. |
| 89 | - **Crawl artifacts** (robots.txt, sitemap.xml, llms.txt) and a public vhost that serves the site |
| 90 | while `/api/v1/*` stays loopback-only. |
| 91 | |
| 92 | ## 2026-10-08 — the check engine and the pull carriers (T18 steps 2–3) |
| 93 | |
| 94 | - **The watch registry**: `watch.watch@1` in and out, validated with the released contracts; a |
| 95 | create needs a source, an extraction, a condition and an action; ownership is the authority and |
| 96 | another owner's watch is 404. |
| 97 | - **The check engine**: conditional GET (ETag/Last-Modified, a 304 costs no body), HEAD-first when |
| 98 | only "did it change?" matters, an unchanged body recorded as `no_change`, and a recorded state for |
| 99 | every terminal path with backoff and `Retry-After`. |
| 100 | - **Carriers**: `http`, `feed` (RSS 2.0 / RSS 1.0 / Atom 1.0) and `api` (JSON/XML, mapped), all |
| 101 | through the one SSRF-guarded fetcher with per-host spacing. |
| 102 | - **Extraction and conditions**: text, html, a small CSS subset, json/jsonpath and regex; `changed`, |
| 103 | the numeric and string predicates, `for_sec` debounce, tolerance and percent. |
| 104 | - **Observations and check runs**: immutable rows with value hashes, capped snapshots and retention. |
| 105 | - **The API** (bearer tokens, one capability per route), the `watch.*` events through the |
| 106 | transactional outbox, and per-actor limits. |
| 107 |