{ "repository": "OpenVibers/OpenVibe.Watch", "stage": "alpha", "code": true, "wave": "W18", "plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026) — T18 Watch complete, steps 2-3 (registry, engine, pull carriers) and step 8 (the public site)", "runtime": "node 22, express, PostgreSQL 18 (openvibe-sdk/db, async) + Valkey 9, fast-xml-parser", "port": 4730, "domain": "openvibe.watch (a public server-rendered site + a loopback-only API; vhost in deploy/nginx; README \"Public site\" and \"Public host\")", "contracts": "openvibe-contracts v0.419.0 (watch.rate.* events; actor-agent requires v0.143.0)", "sdk": "openvibe-sdk v0.161.0", "shared": "openvibe-shared v4.237.0", "capabilities": [ "watch.watch.read", "watch.watch.manage", "watch.observation.read", "watch.check.run", "watch.resource.read" ], "capabilitiesRegistered": true, "contractsUsed": [ "watch.watch@1, watch.watch-request@1, watch.watch-result@1", "watch.observation@1, watch.check-run@1", "watch.observation.recorded@1, watch.watch.triggered@1, watch.check.failed@1", "common.resource-summary@1, common.resource-list-result@1" ], "carriers": [ "http", "feed", "api" ], "carriersPending": [ "event (step 5)", "webhook (step 5)", "run (step 6)", "node (step 7)" ], "actionKinds": [ "notification", "event", "webhook", "actor-task", "actor-agent", "codes-run", "run-job" ], "seededWatches": 0, "deployed": true, "deployment": "openvibe-ovh: unit openvibe-watch.service on 127.0.0.1:4730 (release layout /opt/openvibe.watch, env /etc/openvibe/watch.env, database ov_watch), deployed with `sudo ovhost deploy watch`; the public site openvibe.watch (sign-in, watch management, plan T18 step 8) has been live since 2026-10-08. On 2026-10-10 it held no watches yet, so no check has run against a real site in production.", "works": [ "the watch registry with owner scoping (404 for another owner) validated against the released contracts", "the check engine: conditional GET (ETag/Last-Modified, HEAD-first for change-only), 304/unchanged-body short circuits, recorded states, backoff and Retry-After", "SSRF-guarded fetching (ports, private hosts, every redirect hop, byte cap, one deadline) and per-host spacing", "the http, feed (RSS/Atom) and api carriers", "extraction (text, html, css subset, json/jsonpath, regex) and conditions (changed/threshold/string, for_sec)", "observations with value hashes, capped snapshots and retention; check runs with the full state set", "the loopback-only API (one capability per route), watch.* events through openvibe-sdk's transactional service_outbox, per-actor limits", "the public site: sign-in with OpenVibe.Network (OAuth2 + PKCE S256, sessions in Watch's database), the home page and four starting points, /watches (paged), the create and edit forms (server-side fieldsets, validation errors that keep the input), one watch with pause/resume/delete/check-now, the observation history and check runs, /how-it-works, /updates, and robots/sitemap/llms.txt", "the site calls the registry in-process with the API's ownership rules (another person's watch is 404 on every page and form) and refuses cross-site writes" ], "notYet": [ "no check has run against a real external site in production yet: on 2026-10-10 nobody had created a watch (the tests use stub sites only)", "the event and webhook carriers (step 5), the Run rung (step 6), Link probes (step 7) and the actions (step 4): a watch of such a kind is accepted but never fetched", "an ai extraction is not a check (step 10); such a watch is recorded skipped and nothing is fetched", "the site offers only page/price/feed/json watches: the kinds and the AI reading above are not in the form", "notifications are the watch.watch.triggered event OpenVibe.Network turns into WATCH_TRIGGERED; no other action is dispatched" ], "features": [ "watch registry with owner scoping (404 for another owner) validated against the released contracts", "binding carrier preference order: webhook/event → ETag/Last-Modified → feed → API → Run", "conditional GET: If-None-Match/If-Modified-Since, HEAD-first when only change is needed, 304 and unchanged-body short circuits", "SSRF-guarded fetcher (ports, private hosts, every redirect hop, byte cap, one deadline) and per-host spacing", "feed (RSS/Atom) and mapped-API carriers", "extraction: text, html, css subset, json/jsonpath, regex; ai refused as a later step", "conditions: changed/threshold with tolerance and percent, for_sec debounce over the observation trail", "observations with value hashes, capped snapshots and retention", "check runs with the full terminal-state set, exponential backoff and Retry-After", "POST /api/v1/watches/:id/check plus the observations and checks reads", "GET /api/v1/resources and GET /api/v1/resources/:ovrn (watch.resource.read)", "watch.* events through openvibe-sdk's service_outbox in the same transaction as the state change", "per-actor limits (openvibe-sdk/limits)", "usage readings (check operations): one platform.usage-sample@1 check-operation reading per completed check run, relayed to OpenVibe.Billing (billing.usage.record) by openvibe-sdk/usage; off by default (WATCH_BILLING_INTERVAL_MS)", "the public site: Network sign-in (PKCE S256, web_sessions with only the token hash), server-rendered pages complete without JavaScript, the four create templates, watch management, observation history and check runs, how-it-works and the changelog, robots/sitemap/llms.txt", "provider-rate evidence (plan P6, WATCH_PROVIDER_RATES off by default): DeepInfra and Featherless AI official price documents parsed into integer nano-USD-per-million supply rate observations (D1), a pending Watch change when a rate differs, and a person's approval recorded as evidence only — no rate card is written and nothing is billable" ], "updated": "2026-11-13" }