@celilo/cli 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CELILO_CORE_MODULES.md +3 -0
- package/CELILO_SUBSYSTEMS.md +71 -2
- package/docs/ALERTING.md +298 -0
- package/docs/INDEX.md +103 -0
- package/drizzle/0016_trusted_sources.sql +10 -0
- package/drizzle/0017_alerting.sql +127 -0
- package/drizzle/meta/_journal.json +15 -1
- package/package.json +3 -2
- package/schemas/system_config.json +9 -0
- package/src/ansible/inventory.ts +2 -2
- package/src/cli/commands/alerts-act.ts +107 -0
- package/src/cli/commands/alerts-list.ts +62 -0
- package/src/cli/commands/alerts-poll.ts +129 -0
- package/src/cli/commands/alerts-sweep.ts +156 -0
- package/src/cli/commands/module-list.ts +50 -3
- package/src/cli/commands/monitor.ts +178 -0
- package/src/cli/commands/notify-config.ts +453 -0
- package/src/cli/commands/system-audit.ts +2 -0
- package/src/cli/commands/system-update.ts +1 -0
- package/src/cli/completion.ts +26 -0
- package/src/cli/generate-zsh-completion.ts +2 -0
- package/src/cli/index.ts +58 -0
- package/src/cli/tui/audit-state.ts +2 -0
- package/src/db/schema.ts +358 -0
- package/src/hooks/capability-loader.ts +158 -46
- package/src/hooks/capability-map-coverage.test.ts +101 -0
- package/src/manifest/schema.ts +60 -1
- package/src/services/alerting/ack.test.ts +212 -0
- package/src/services/alerting/ack.ts +119 -0
- package/src/services/alerting/builtin-monitors.test.ts +132 -0
- package/src/services/alerting/builtin-monitors.ts +84 -0
- package/src/services/alerting/builtin-source.ts +82 -0
- package/src/services/alerting/coverage-source.ts +38 -0
- package/src/services/alerting/deferral.test.ts +161 -0
- package/src/services/alerting/delivery-loop.test.ts +396 -0
- package/src/services/alerting/deploy-hooks.test.ts +125 -0
- package/src/services/alerting/deploy-hooks.ts +111 -0
- package/src/services/alerting/escalation.test.ts +207 -0
- package/src/services/alerting/escalation.ts +151 -0
- package/src/services/alerting/format.test.ts +193 -0
- package/src/services/alerting/format.ts +150 -0
- package/src/services/alerting/health-coverage.ts +81 -0
- package/src/services/alerting/inbound-poller.test.ts +298 -0
- package/src/services/alerting/inbound-poller.ts +236 -0
- package/src/services/alerting/inbound.test.ts +201 -0
- package/src/services/alerting/inbound.ts +112 -0
- package/src/services/alerting/interview-responder.test.ts +169 -0
- package/src/services/alerting/interview-responder.ts +158 -0
- package/src/services/alerting/keys.test.ts +155 -0
- package/src/services/alerting/keys.ts +190 -0
- package/src/services/alerting/monitors.ts +185 -0
- package/src/services/alerting/notification-responder.test.ts +290 -0
- package/src/services/alerting/notification-responder.ts +260 -0
- package/src/services/alerting/notifier.ts +219 -0
- package/src/services/alerting/people.ts +178 -0
- package/src/services/alerting/quiet-hours.test.ts +140 -0
- package/src/services/alerting/quiet-hours.ts +99 -0
- package/src/services/alerting/reconcile.test.ts +190 -0
- package/src/services/alerting/reconcile.ts +166 -0
- package/src/services/alerting/run-monitor.test.ts +185 -0
- package/src/services/alerting/run-monitor.ts +177 -0
- package/src/services/alerting/store.test.ts +222 -0
- package/src/services/alerting/store.ts +289 -0
- package/src/services/alerting/suppression.test.ts +228 -0
- package/src/services/alerting/suppression.ts +142 -0
- package/src/services/alerting/sweep-runner.test.ts +229 -0
- package/src/services/alerting/sweep-runner.ts +204 -0
- package/src/services/alerting/sweep.test.ts +61 -0
- package/src/services/alerting/sweep.ts +41 -0
- package/src/services/alerting/tokens.test.ts +152 -0
- package/src/services/alerting/tokens.ts +119 -0
- package/src/services/alerting/transport-loader.ts +48 -0
- package/src/services/aspect-runner.ts +2 -2
- package/src/services/audit/index.test.ts +1 -0
- package/src/services/audit/index.ts +3 -0
- package/src/services/audit/trusted-sources.test.ts +137 -0
- package/src/services/audit/trusted-sources.ts +124 -0
- package/src/services/audit/types.ts +2 -1
- package/src/services/firewall-reach.ts +83 -0
- package/src/services/health-runner.test.ts +50 -0
- package/src/services/health-runner.ts +116 -82
- package/src/services/module-deploy.ts +32 -3
- package/src/services/ssh-key-manager.test.ts +14 -0
- package/src/services/ssh-key-manager.ts +12 -0
- package/src/services/system-config-validator.test.ts +31 -1
- package/src/services/trusted-sources.test.ts +221 -0
- package/src/services/trusted-sources.ts +159 -0
- package/src/services/update/orchestrator.test.ts +1 -0
- package/src/templates/generator.ts +6 -29
package/CELILO_CORE_MODULES.md
CHANGED
|
@@ -26,10 +26,12 @@ Each entry: `module id` — what it is — **provides** / **requires** capabilit
|
|
|
26
26
|
- **knot-unbound-internal** — split-horizon internal DNS via Knot (authoritative) + Unbound (recursive); lightweight, plain apt, no .NET. **provides:** `dns_internal`. Ships a base-module-aspect (`modules/knot-unbound-internal/base-module-aspect/`).
|
|
27
27
|
- **technitium** — internal split-horizon DNS resolver + authoritative server (web UI + HTTP API); heavier alternative to knot-unbound. **provides:** `dns_internal`. Ships a base-module-aspect (`modules/technitium/base-module-aspect/`).
|
|
28
28
|
- **namecheap** — public DNS A-record management via Namecheap Dynamic DNS API (HTTP, no browser automation). **provides:** `dns_registrar`.
|
|
29
|
+
- **wireguard** — owns the admin WireGuard tunnel on the firewall host: interface, listen port, peers (as records), and client subnet are module config rather than hand-maintained state. Exposes the listen port and **registers the client subnet as a trusted source**, so VPN reach into the managed zones is in the firewall registry and every converge re-emits it. Records `network.vpn.subnet`, which the internal resolver's split-horizon view also consumes. Adopts a running tunnel in place (existing key and peers retained; `wg syncconf`, never `wg-quick down`) because that tunnel is the operator's recovery path. **requires:** `firewall` (and the provider must support trusted-source registration — `iptables` does, `greenwave` does not).
|
|
29
30
|
|
|
30
31
|
## Public edge (ingress / identity)
|
|
31
32
|
|
|
32
33
|
- **caddy** — reverse proxy with automatic HTTPS (HTTP-01 ACME); the standard HTTPS ingress. **provides:** `public_web`. **requires:** `dns_registrar`, `firewall`.
|
|
34
|
+
- **generic-cpanel-hosting-provider** — publishes static sites into a subfolder of a domain on a cPanel/SSH web host celilo does **not** govern (the host owns the domain, DNS, TLS and web server). Systemless like **namecheap** — no `requires.system`, no zone, no IPAM; it holds credentials for an external party. One provider serves many accounts, resolved by hostname; onboarding takes the account password once, `ssh-copy-id`s celilo's key, then discards it. **provides:** `external_web`.
|
|
33
35
|
- **authentik** — Authentik identity provider with OIDC (Docker Compose: server, worker, Postgres, Redis). **provides:** `idp`. **requires:** `public_web`, `dns_registrar`, `firewall`.
|
|
34
36
|
|
|
35
37
|
## Celilo's own infrastructure (self-hosted)
|
|
@@ -37,6 +39,7 @@ Each entry: `module id` — what it is — **provides** / **requires** capabilit
|
|
|
37
39
|
- **celilo-mgmt** — the celilo management server itself, deployed as a module (replaces install.sh + `system init`; ships daemon, runs migrations, self-registers). **provides:** `celilo_event_bus`, `celilo_module_deploy_worker`. **requires:** `cross_module_read`. See `openspec/specs/management-as-module/spec.md`.
|
|
38
40
|
- **celilo-registry** — module registry server (Cargo sparse protocol); stores `.netapp` files, serves index + search/download API. On install it provisions a confidential introspection OIDC client via `idp.create_oidc_client` (SECURE_MODULE_PUBLISH.md §5[D-A]) and converges its issuer + introspection endpoint + creds onto the box for RFC 7662 token verification. **provides:** `registry_publish`. **requires:** `public_web`, `dns_registrar`, `idp`.
|
|
39
41
|
- **celilo-apt-repo** — Debian apt repository (reprepro + Bun HTTP server) serving the celilo `.deb` at apt.celilo.computer. **provides:** `apt_publish`. **requires:** `public_web`, `dns_registrar`.
|
|
42
|
+
- **signal** — bidirectional Signal transport for alerts and deploy-interview questions; runs signal-cli in daemon mode with its JSON-RPC socket bound to the host's own address (never public). Enrolled as a SECONDARY DEVICE of an existing Signal account rather than registering its own number — Signal blocks most VOIP ranges and bans bot-ish registrations. Recipient addresses live on celilo routes, not in module config, so adding a person never requires a redeploy. Runs on x86_64 and aarch64. `libsignal-client` ships no linux-aarch64 native, so celilo builds one (`modules/signal/build/`) and installs it as a `libsignal-jni` .deb on ARM hosts; x86_64 uses the JAR's bundled native. **provides:** `notification` (`send`, `receive`). **requires:** nothing — a transport that depended on the proxy, registrar or firewall could not tell you those were broken. See `openspec/changes/add-alerting/`.
|
|
40
43
|
- **celilo-website** — public docs site (static Astro) served via Caddy on celilo.computer. **requires:** `public_web`, `dns_registrar`.
|
|
41
44
|
|
|
42
45
|
## Git forge & CI pipeline
|
package/CELILO_SUBSYSTEMS.md
CHANGED
|
@@ -45,13 +45,39 @@ see `openspec/specs/`. Companion doc: [CELILO_CORE_MODULES.md](./CELILO_CORE_MOD
|
|
|
45
45
|
| `public_web` | `packages/capabilities/src/public-web.ts` (`createPublicWeb`). **Route lifecycle** is framework-owned at both ends, not per-module: removing a consumer runs `apps/celilo/src/services/web-route-cleanup.ts` (delete rows → reclaim `/srv/www/<slug>` → emit `routes_changed`) whether or not the module has an `on_uninstall`; deploying a provider runs `apps/celilo/src/services/public-web-republish.ts` (re-run every static consumer's `on_install`) so a provider rebuild refills the web roots it destroyed. | caddy |
|
|
46
46
|
| `idp` | `packages/capabilities/src/idp.ts` | authentik |
|
|
47
47
|
| `dns_registrar` | `packages/capabilities/src/dns-registrar.ts` (`registerHost`) | namecheap |
|
|
48
|
+
| `external_web` | `packages/capabilities/src/external-web.ts` (`publishStaticSite`). **OFF-FLEET static publishing** — the counterpart to `public_web`. See the in-fleet vs off-fleet note below. | generic-cpanel-hosting-provider |
|
|
48
49
|
| `firewall` | `packages/capabilities/src/firewall.ts` (`exposeService`, `unexposeService`, `listExposedServices`). **Converge model**: register into the shared-core port-forward registry → render the complete ruleset → apply atomically (see the firewall converge note below). | greenwave, iptables |
|
|
49
50
|
| `dns_internal` | `packages/capabilities/src/dns-internal.ts` | knot-unbound-internal, technitium |
|
|
50
51
|
| `dhcp_server` | `packages/capabilities/src/dhcp-server.ts` | greenwave |
|
|
51
52
|
| `source_forge` | `packages/capabilities/src/source-forge.ts` | forgejo |
|
|
52
53
|
| `registry_publish` | `packages/capabilities/src/registry-publish.ts` | celilo-registry |
|
|
54
|
+
| `notification` | `packages/capabilities/src/notification.ts` (`send`, optional `receive`) | signal (planned) — transports are ordinary modules, both self-hosted and credential-only |
|
|
53
55
|
| `cross_module_read` | `packages/capabilities/src/cross-module-read.ts` | framework (read other modules' capability data) |
|
|
54
56
|
|
|
57
|
+
### In-fleet (`public_web`) vs off-fleet (`external_web`)
|
|
58
|
+
|
|
59
|
+
Both serve a static site; the difference is **control, not file serving**. A
|
|
60
|
+
system is *in-fleet* when celilo governs it — it can run a managed daemon, bind
|
|
61
|
+
a port, drive the firewall, put the box on the VPN. It is **off-fleet** when
|
|
62
|
+
celilo can only place files there: an unprivileged, jailed cPanel account is the
|
|
63
|
+
canonical case. Off-fleet is a governance boundary, not a deploy boundary.
|
|
64
|
+
|
|
65
|
+
| | `public_web` (caddy) | `external_web` |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| TLS certificate | celilo obtains it (ACME) | already installed by the host |
|
|
68
|
+
| Public + split-horizon DNS | celilo registers | the host's |
|
|
69
|
+
| Firewall ingress | celilo opens | not celilo's to open |
|
|
70
|
+
| `idp` / reverse-proxy composition | yes | no |
|
|
71
|
+
| Route | any path, incl. `/` | a subfolder only; `/` is REJECTED (the docroot holds the host's own site) |
|
|
72
|
+
|
|
73
|
+
`external_web` is module-owned (loaded through `CAPABILITY_MODULE_MAP` like
|
|
74
|
+
`dns_registrar`), not framework-owned like `createPublicWeb` — there is no
|
|
75
|
+
celilo-side machinery to own, only a provider holding credentials. Its
|
|
76
|
+
`publishStaticSite` signature is a subset of `public_web`'s on purpose, so a
|
|
77
|
+
content module targets either with
|
|
78
|
+
`capabilities.external_web ?? capabilities.public_web`. Both are
|
|
79
|
+
**authoritative**: a publish that doesn't reach clients throws (#328).
|
|
80
|
+
|
|
55
81
|
## Remote-ops primitives (the SSH seam — modules never hand-build SSH)
|
|
56
82
|
|
|
57
83
|
Module hooks reach a remote box ONLY through these typed primitives
|
|
@@ -59,16 +85,17 @@ Module hooks reach a remote box ONLY through these typed primitives
|
|
|
59
85
|
`modules/**/scripts/` is a defect. Impl: `packages/capabilities/src/remote.ts`
|
|
60
86
|
(exported from `packages/capabilities/src/index.ts`).
|
|
61
87
|
|
|
62
|
-
- **`remoteExec`** — the ONE ssh seam (`ssh
|
|
88
|
+
- **`remoteExec`** — the ONE ssh seam (`ssh <user>@<target> <cmd>`); everything else builds on it. `user`/`port`/`identityFile` on the target default to `root`/22/the agent key, so a fleet call is unchanged; an OFF-FLEET account (`external_web`) sets them.
|
|
63
89
|
- **`probe`** — read-only health checks: `systemd` / `http` / `command`.
|
|
64
90
|
- **`serviceCtl`** — systemctl start/stop/restart/reload/enable/disable.
|
|
65
91
|
- **`runAppCommand` / `runAppCommandWithSecret`** — escape-hatch on-box command; the secret variant feeds the secret on **stdin** (`$SECRET`), never argv.
|
|
66
92
|
- **`streamBackup` / `streamRestore` / `fetchFile` / `pushFile`** — binary-safe streaming via local shell redirect/pipe.
|
|
67
93
|
- **`waitFor`** — predicate-poll combinator.
|
|
68
94
|
- **`applyRenderedConfig`** — converge: write rendered config → validate → apply → rollback (one round-trip). Used by caddy (Caddyfile), knot (views), iptables (ruleset).
|
|
95
|
+
- **`installAuthorizedKey`** — ONE-TIME credential bootstrap for an off-fleet account: `ssh-copy-id` under a password taken from the child ENV (`SSH_ASKPASS_REQUIRE=force`, so no `sshpass` dependency and the password never lands in a command string). Idempotent; used by `external_web` onboarding so every later publish is key-based.
|
|
69
96
|
- **`tailLog` / `grepLog`** — journald reads (regex / ignoreCase).
|
|
70
97
|
|
|
71
|
-
Target = `RemoteTarget` (`{ ipv4_address }`; `DeployedSystem` satisfies it). The
|
|
98
|
+
Target = `RemoteTarget` (`{ ipv4_address }` plus optional `user` / `port` / `identityFile`; `DeployedSystem` satisfies it). The
|
|
72
99
|
runner seam (`execRunner` real / `createMockRunner` for tests) lives in
|
|
73
100
|
`packages/capabilities/src/testing.ts`. Authoring guide: `MODULE_PRIMITIVES.md`
|
|
74
101
|
(ships in `@celilo/cli`).
|
|
@@ -88,6 +115,27 @@ runner seam (`execRunner` real / `createMockRunner` for tests) lives in
|
|
|
88
115
|
`exposeService`/`unexposeService` register into the store, then render + apply
|
|
89
116
|
atomically via `applyRenderedConfig` (`iptables-restore`). Replaces the old
|
|
90
117
|
per-rule `iptables -A`; the registry (not `iptables -L`) is the source of truth.
|
|
118
|
+
- **Trusted-source registry (desired state)** — `trusted_sources` DB table
|
|
119
|
+
(migration `0016`) + `apps/celilo/src/services/trusted-sources.ts`
|
|
120
|
+
(`buildTrustedSourceStore`, `composeTrustedSubnets`). The sibling primitive to
|
|
121
|
+
port forwards, for the case a port forward cannot express: an ORIGIN subnet
|
|
122
|
+
permitted to initiate into every managed zone. Registered through the OPTIONAL
|
|
123
|
+
`registerTrustedSource` / `withdrawTrustedSource` / `listTrustedSources` trio on
|
|
124
|
+
`packages/capabilities/src/firewall.ts`; `iptables` implements it, `greenwave`
|
|
125
|
+
deliberately does not. A consumer detects support with `supportsTrustedSources`
|
|
126
|
+
and fails loudly via `requireTrustedSources` — a silent no-op is prohibited by
|
|
127
|
+
the contract, because a discarded registration yields a network that believes
|
|
128
|
+
it has reach it does not have.
|
|
129
|
+
- **Composed trusted subnets** — `apps/celilo/src/hooks/capability-loader.ts`
|
|
130
|
+
(`loadTrustedSubnets`): derived control plane + module registrations + the
|
|
131
|
+
`firewall.trusted_subnets` operator override, deduped and origin-tagged. With
|
|
132
|
+
no contributors it equals the derived subnet alone, so the rendered ruleset is
|
|
133
|
+
byte-identical to the pre-composition output.
|
|
134
|
+
- **Unowned-trusted-network audit** — `apps/celilo/src/services/audit/trusted-sources.ts`
|
|
135
|
+
+ `apps/celilo/src/services/firewall-reach.ts`: reads each firewall's LIVE table
|
|
136
|
+
(`iptables-save`) and reports reach granted to a network celilo does not
|
|
137
|
+
recognise, instead of removing it silently at the next converge. Surfaces under
|
|
138
|
+
`celilo system audit` as the `trusted_sources` category.
|
|
91
139
|
|
|
92
140
|
## Hooks & deploy
|
|
93
141
|
|
|
@@ -114,6 +162,26 @@ runner seam (`execRunner` real / `createMockRunner` for tests) lives in
|
|
|
114
162
|
- **Registry token verification (opaque + idp introspection)** — `packages/registry-server/src/auth.ts` (`TokenAuth` — opaque SHA-256 publish tokens, admin/per-package scope) + `packages/registry-server/src/introspection.ts` (`IntrospectionVerifier` — RFC 7662 verify-bridge, SECURE_MODULE_PUBLISH.md §5[D-A]). `authorizePackage()` in `server.ts` tries the opaque set first (unchanged), then, for a token unknown to it, `identify()`s it via the idp introspection endpoint using the registry's confidential OIDC client creds (`OIDC_INTROSPECTION_ENDPOINT`/`OIDC_CLIENT_ID`/`OIDC_CLIENT_SECRET`, provisioned on install — ce-7aa), reading `{active, sub, groups, exp}`. Fails CLOSED on any introspection error; never logs tokens/secrets. Instant revocation: revoke at the idp → next publish sees `active:false` → 401.
|
|
115
163
|
- **Registry module-owner table (hybrid group + owner authz — ce-1ch, D-C)** — `packages/registry-server/src/module-owner-store.ts` (`ModuleOwnerStore` — JSON-persisted `{moduleName, ownerSub, claimedAt, sourceGroup}`, `REGISTRY_OWNERS_FILE`/`dataDir/module-owners.json`). The verified `groups` claim gates *whether* an identity may publish (`REGISTRY_ADMIN_GROUP`→publish/reassign anything; `REGISTRY_PUBLISHER_GROUP`, default `celilo-authors`, configurable→claim+publish owned); the owner table gates *which names*. First-publish-claims: the first verified publisher of an unclaimed name owns it; a *different* publisher is then DENIED (confused-deputy defense — Author-A cannot publish Author-B's module). Admin HTTP endpoints `GET /api/v1/modules/owners`, `GET|POST /api/v1/modules/owners/{name}` (reassign). Operator front door: `celilo registry owner list|show|set` (`apps/celilo/src/cli/commands/registry-owner.ts`), admin token resolved from the local `publish_tokens` bootstrap list.
|
|
116
164
|
|
|
165
|
+
## Alerting & notifications
|
|
166
|
+
|
|
167
|
+
Observation and delivery: monitors run checks on a schedule, alerts hold what
|
|
168
|
+
is currently wrong, and routes carry the message to a person's phone. Design:
|
|
169
|
+
`openspec/changes/add-alerting/design.md`.
|
|
170
|
+
|
|
171
|
+
- **Alert identity** — `apps/celilo/src/services/alerting/keys.ts` — the key grammar (`module:<id>[/check:<name>]`, `builtin:<check>[/<kind>:<target>]`) that makes "the same problem" the same alert across runs. `moduleAlertKey`, `moduleCheckAlertKey`, `builtinAlertKey`, `parseAlertKey`.
|
|
172
|
+
- **Reconciliation** — `apps/celilo/src/services/alerting/reconcile.ts` (`reconcile`) — a successful run's failing-key set is authoritative and resolution is by SET DIFFERENCE (absent ⇒ resolved). A run whose outcome is `error` resolves NOTHING and fires a module-level alert instead: the false-all-clear guard.
|
|
173
|
+
- **Monitor execution** — `apps/celilo/src/services/alerting/run-monitor.ts` (`runOneMonitor`) + `sweep.ts` (`selectDueMonitors`) + `builtin-monitors.ts` / `health-coverage.ts` (the built-in checks and the "module with no health check" coverage check).
|
|
174
|
+
- **The sweep** — `apps/celilo/src/services/alerting/sweep-runner.ts` (`runSweep`) — the ordered pass that makes alerting run by itself: run due monitors → promote past-grace alerts → re-evaluate suppression → flush quiet-hours deferrals → notify. Driven by `celilo alerts sweep` on `timer.tick.5m`. Never throws for one bad monitor.
|
|
175
|
+
- **Suppression (topology-derived, never configured)** — `apps/celilo/src/services/alerting/suppression.ts` — `ancestorKeysFor`/`findSuppressor`/`machineAlertKey`. A firing machine explains its modules' failures; a firing capability provider explains its zone's consumers. Derived from `module_systems`, so it cannot drift from reality. Deploy windows: `deploy-hooks.ts` (`openDeployWindow`/`closeDeployWindows` — closed by module, so a crashed deploy self-heals).
|
|
176
|
+
- **Escalation & quiet hours** — `escalation.ts` (`decideEscalation`, every reason to stay silent enumerated) + `quiet-hours.ts` (`isWithinQuietHours`, Intl-based and DST-safe). Quiet hours defer the MESSAGE while the escalation clock keeps running.
|
|
177
|
+
- **Delivery** — `notifier.ts` (`notifyAlert`, `deliverDeferred`, `notifyResolved`, body composition) + `transport-loader.ts` (`loadNotificationTransport` — resolves a route's transport module's `notification` capability).
|
|
178
|
+
- **Inbound replies** — `tokens.ts` (per-DELIVERY reply tokens, so a reply identifies WHO) + `inbound.ts` (`interpretInbound` — two-factor: valid unexpired token AND sender matching the issuing route) + `inbound-poller.ts` (`pollInbound`, `makeReceiver`). celilo-mgr polls the transport; nothing calls in, so the ack path never depends on the public HTTPS it may be paging about.
|
|
179
|
+
- **Ack / silence / resolve** — `ack.ts` — three deliberately distinct operations. An ack is broadcast to every other paged route.
|
|
180
|
+
- **Interviews over a transport** — `interview-responder.ts` (delivery policy; `secret.*` families are REFUSED) + `notification-responder.ts` (queries the bus's events table for unanswered questions: a watch only sees events emitted after it registers, and `alerts poll` is one-shot. Leaves a question alone for 90s so an attached responder — terminal, or the `api-serve` wire bridge — answers first). Alerts and interviews share the send path, the token table, and the inbound path; only `targetId` differs.
|
|
181
|
+
- **People, routes, policies** — `people.ts` — `createPerson`/`createRoute`/`createPolicy`/`addPolicyStep`.
|
|
182
|
+
- **Persistence** — `apps/celilo/src/services/alerting/store.ts`; tables `people`, `routes`, `escalation_policies`, `escalation_steps`, `monitors`, `monitor_runs`, `suppression_windows`, `alerts`, `notification_deliveries` (`apps/celilo/src/db/schema.ts`, migration `drizzle/0017_alerting.sql`).
|
|
183
|
+
- **CLI** — `apps/celilo/src/cli/commands/` — `alerts-list.ts`, `alerts-act.ts` (ack/silence/resolve), `alerts-sweep.ts`, `alerts-poll.ts`, `monitor.ts`, `notify-config.ts` (`person`/`route`/`escalation-policy`).
|
|
184
|
+
|
|
117
185
|
## Persistence
|
|
118
186
|
|
|
119
187
|
- **DB schema** — `apps/celilo/src/db/schema.ts`. Client: `apps/celilo/src/db/client.ts`. Migration runner: `apps/celilo/src/db/migrate.ts`. Migrations: `apps/celilo/drizzle/`.
|
|
@@ -141,4 +209,5 @@ Run any celilo command on celilo-mgr over SSH instead of screen-scraping `ssh <h
|
|
|
141
209
|
|
|
142
210
|
- **cele2e harness** — `packages/e2e/src/` — `runner.ts`, `container-manager.ts` (`startNetwork`, `reconnectNetwork`), `network-builder.ts` (`NetworkBuilder`).
|
|
143
211
|
- **Run wrapper** — `infra/scripts/cele2e-run.sh` lives in the separate `infra/` clone, **not** in this repo. See the cele2e section of the repo-root `CLAUDE.md` for the operator workflow.
|
|
212
|
+
- **Signal simulators** — three containers, three jobs. `docker/Dockerfile.signal-cli` runs the REAL unlinked daemon (`network().withSignalCli()`, reachable at `signal-cli.lab`) so `e2e/tests/signal-contract.test.ts` can re-check celilo's understanding of the JSON-RPC surface against the actual binary — the pebble pattern. `docker/Dockerfile.signal-sim` runs `simulators/signal-cli/server.ts` (`withSignalSim()`, `signal-sim.lab`), the drivable stand-in with a control surface (`/_control/inbound`, `/_control/sent`, `/_control/unlink`). `docker/Dockerfile.signal-release` (`withSignalRelease()`, `signal-release.lab`) serves the signal-cli release tarball so the module's deploy-time download resolves inside the sealed network — the download is served, never skipped. The libsignal aarch64 native is compiled at `build-infra` time from the module's own recipe (`packages/e2e/scripts/stage-libsignal.ts` → `modules/signal/build/`) and staged into the apt-repo pool, so the recipe is exercised for real on every rebuild while the deploy stays fast and the network stays sealed.
|
|
144
213
|
- **MCP server (agent-driven e2e)** — `packages/mcp-server/src/index.ts` — stdio MCP server exposing `start_run`/`run_status`/`run_result`/`stop_run` (detached cele2e runs read off the event bus, no ANSI scraping) + `env_check` (docker VM, run-lock, shared-infra, mgmt image CLI version, netapps). Dev/ops tool, `private`, not shipped to consumers.
|
package/docs/ALERTING.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# Alerting — operator guide
|
|
2
|
+
|
|
3
|
+
Getting celilo to tell you when something is wrong, on a channel you actually
|
|
4
|
+
read, without teaching you to ignore it.
|
|
5
|
+
|
|
6
|
+
This guide is task-ordered: set up who gets told, how they get told, and in
|
|
7
|
+
what order; then watch a module; then handle an alert when it arrives.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The shape of it
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
monitor ──runs a check every N minutes──▶ alert ──escalation policy──▶ route ──▶ your phone
|
|
15
|
+
│ │ │
|
|
16
|
+
└─ a module's health_check hook, └─ one per PROBLEM, not per run, └─ you reply
|
|
17
|
+
or a built-in (machines_reachable) identified by a stable key `ack`
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Four nouns, and they are worth keeping straight:
|
|
21
|
+
|
|
22
|
+
| noun | what it is |
|
|
23
|
+
|------|-----------|
|
|
24
|
+
| **person** | a human, with a timezone and optional quiet hours |
|
|
25
|
+
| **route** | one way to reach a person (a Signal number). A person may have several |
|
|
26
|
+
| **escalation policy** | an ordered list of *steps*: which route, how many minutes in |
|
|
27
|
+
| **monitor** | a check that runs on a schedule and produces alerts |
|
|
28
|
+
|
|
29
|
+
An alert's identity is its **key** — `module:caddy/check:cert-validity`. The
|
|
30
|
+
same problem across a hundred runs is one alert, so a slow-burning failure
|
|
31
|
+
does not become a hundred messages.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Add yourself
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
celilo person add peba --timezone America/Los_Angeles
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Quiet hours are optional and per-person. They defer the *message*, never the
|
|
42
|
+
escalation clock — steps keep advancing while you sleep, so if nobody
|
|
43
|
+
acknowledges by 03:00 the chain has already reached whoever is awake:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
celilo person add peba --timezone America/Los_Angeles \
|
|
47
|
+
--quiet-hours 22:00-07:00
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A policy may be marked to page through quiet hours anyway
|
|
51
|
+
(`escalation_policies.bypass_quiet_hours`); `celilo escalation-policy list`
|
|
52
|
+
shows which do. Use it sparingly — a channel that wakes you for a warning is a
|
|
53
|
+
channel you will mute.
|
|
54
|
+
|
|
55
|
+
## 2. Add a route
|
|
56
|
+
|
|
57
|
+
A route needs a transport module. Deploy `signal` first (see
|
|
58
|
+
[the signal module's README](../../../modules/signal/README.md)); then:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
celilo route add peba signal --address +15551234567 --can-ack
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`--can-ack` says replies from this address are trusted to acknowledge alerts.
|
|
65
|
+
A route without it is send-only — it still gets paged, it just gets no reply
|
|
66
|
+
token, because a reply instruction nobody can follow is worse than none.
|
|
67
|
+
|
|
68
|
+
`--severity-floor critical` keeps a route out of everything below critical.
|
|
69
|
+
That is how "hazel's phone is for real emergencies only" is expressed.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
celilo route list
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 3. Build an escalation policy
|
|
76
|
+
|
|
77
|
+
Steps are `--after` minutes from when escalation *begins*, not from the
|
|
78
|
+
previous step — so the numbers read as a timeline rather than an accumulation.
|
|
79
|
+
A step names a person and a transport, which together identify the route.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
celilo escalation-policy add oncall
|
|
83
|
+
celilo escalation-policy step oncall peba signal --after 0
|
|
84
|
+
celilo escalation-policy step oncall hazel signal --after 15
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
That is: page me immediately; if nobody has acknowledged 15 minutes later,
|
|
88
|
+
page hazel. A step whose route's severity floor excludes the alert is
|
|
89
|
+
skipped *immediately* rather than costing its delay in silence.
|
|
90
|
+
|
|
91
|
+
## 4. Watch something
|
|
92
|
+
|
|
93
|
+
A module that declares `hooks.health_check` gets a monitor automatically on
|
|
94
|
+
its first deploy. To see what is watched:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
celilo monitor list
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
To add one by hand, and to point it at a policy:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
celilo monitor add caddy --interval 5m
|
|
104
|
+
celilo monitor add machines_reachable --interval 5m
|
|
105
|
+
celilo escalation-policy assign oncall caddy
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The target says which kind it is: an audit category (`machines_reachable`,
|
|
109
|
+
`health_coverage`) is a built-in check, anything else is a module's
|
|
110
|
+
`health_check` hook. `celilo monitor run <target>` runs one now.
|
|
111
|
+
|
|
112
|
+
`machines_reachable` is deliberately **unsuppressible**: it is how you find
|
|
113
|
+
out the fleet is unreachable, so nothing may explain it away.
|
|
114
|
+
|
|
115
|
+
Modules with no health check at all show up under the built-in
|
|
116
|
+
`health_coverage` check — "not observed" is a state worth being able to see.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Living with it
|
|
121
|
+
|
|
122
|
+
### What you will see
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
celilo alerts list
|
|
126
|
+
celilo alerts list --json # for scripts
|
|
127
|
+
celilo module list # health column, per module
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The health column reads `healthy` / `failing(n)` / `suppressed` / `not observed`.
|
|
131
|
+
"not observed" is not "healthy" and is not printed as if it were.
|
|
132
|
+
|
|
133
|
+
### When one arrives
|
|
134
|
+
|
|
135
|
+
The message names the key first — on a phone that is what shows in the
|
|
136
|
+
notification, and half-awake you need to know *what* before *why*.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
🔴 module:caddy/check:cert-validity
|
|
140
|
+
certificate for www.example.com expires in 2 days
|
|
141
|
+
|
|
142
|
+
reply K3M9QX
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Reply with the token, or just `ack` if only one alert is outstanding for you.
|
|
146
|
+
Everyone else who was paged is told **who** took it, so the secondary can go
|
|
147
|
+
back to bed.
|
|
148
|
+
|
|
149
|
+
From a terminal:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
celilo alerts ack module:caddy/check:cert-validity --as peba
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### ack vs silence vs resolve
|
|
156
|
+
|
|
157
|
+
These look similar and mean entirely different things. Collapsing any two is
|
|
158
|
+
how an alerting system stops being trusted.
|
|
159
|
+
|
|
160
|
+
| | means | escalation | alert state |
|
|
161
|
+
|---|---|---|---|
|
|
162
|
+
| **ack** | "I have this" | stops | still **firing** — the problem is still happening |
|
|
163
|
+
| **silence** | "I know, stop telling me until X" | stops until expiry | firing, silenced |
|
|
164
|
+
| **resolve** | "the condition is gone" | stops | resolved |
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
celilo alerts silence module:caddy/check:cert-validity --for 2h
|
|
168
|
+
celilo alerts resolve module:caddy/check:cert-validity
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`celilo monitor disable <target>` is the bigger hammer: it stops the check
|
|
172
|
+
running at all, rather than silencing what it finds.
|
|
173
|
+
|
|
174
|
+
Silence always expires — that is what makes it safe to offer.
|
|
175
|
+
|
|
176
|
+
### What `resolve` is actually for
|
|
177
|
+
|
|
178
|
+
Reasonable question: if the check is still failing, what does resolving it
|
|
179
|
+
achieve? Nothing — and that is the point. **Resolve does not override the
|
|
180
|
+
monitor.** The next successful run is authoritative, so if the condition is
|
|
181
|
+
still there the alert comes straight back on the next sweep.
|
|
182
|
+
|
|
183
|
+
Its use is narrower than it looks: **you have just fixed something, and the
|
|
184
|
+
monitor's interval is fifteen minutes.** Without resolve you stare at a stale
|
|
185
|
+
`firing` row — and keep escalating against it — until the next run notices.
|
|
186
|
+
Resolve says "I know this one is done, stop counting" and costs nothing if you
|
|
187
|
+
are wrong, because the monitor corrects you.
|
|
188
|
+
|
|
189
|
+
If you find yourself resolving the same key repeatedly, the check interval is
|
|
190
|
+
too long or the check is wrong. That is the signal, not the resolve.
|
|
191
|
+
|
|
192
|
+
### Replies can only acknowledge
|
|
193
|
+
|
|
194
|
+
The reply grammar is deliberately one verb. A bare token acknowledges; so does
|
|
195
|
+
`ack`, `ok`, `k`, or 👍:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
K3M9QX → acknowledged
|
|
199
|
+
K3M9QX ack → acknowledged
|
|
200
|
+
K3M9QX resolve → REJECTED, and nothing happens
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`resolve` and `silence` are terminal operations, not reply verbs — every extra
|
|
204
|
+
verb is another thing to mistype at 3am, and a reply that silently did the wrong
|
|
205
|
+
thing would be worse than one that did nothing. An unrecognised verb is
|
|
206
|
+
rejected and counted, so `celilo alerts poll` reports it rather than swallowing
|
|
207
|
+
it.
|
|
208
|
+
|
|
209
|
+
### Why didn't I get paged?
|
|
210
|
+
|
|
211
|
+
Every reason to stay silent is recorded rather than being an early return.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
celilo alerts list --json | jq '.[] | {key, state, suppressedBy: .suppressedByAlertId}'
|
|
215
|
+
celilo monitor run caddy # run one check now, see what it reports
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The usual answers:
|
|
219
|
+
|
|
220
|
+
- **within grace** — a new failure waits out a grace window before it may page,
|
|
221
|
+
so a check that flaps for thirty seconds never reaches you.
|
|
222
|
+
- **suppressed** — something upstream explains it. A firing machine explains
|
|
223
|
+
every module on it; a firing capability provider explains its zone's
|
|
224
|
+
consumers. This is derived from the deployment topology, never configured,
|
|
225
|
+
so it cannot drift from reality.
|
|
226
|
+
- **deploy window** — a deploy suppresses its own module. Restarting a service
|
|
227
|
+
fails its own health check, and paging you about the deploy you are
|
|
228
|
+
personally running is how a channel gets muted.
|
|
229
|
+
- **awaiting confirmation** — suppression just lifted. The alert waits for one
|
|
230
|
+
more successful run before paging: if it recovered along with its cause, it
|
|
231
|
+
resolves quietly.
|
|
232
|
+
- **quiet hours** — deferred, not dropped. It arrives when the window ends,
|
|
233
|
+
*if* it is still a problem then.
|
|
234
|
+
|
|
235
|
+
### What happens when the check itself breaks
|
|
236
|
+
|
|
237
|
+
A check that could not **run** tells you nothing about what it was watching.
|
|
238
|
+
So an errored run resolves nothing — every alert it owns is frozen exactly as
|
|
239
|
+
it was — and raises one module-level alert saying the check is broken.
|
|
240
|
+
|
|
241
|
+
The alternative is the single most dangerous failure an alerting system can
|
|
242
|
+
have: reading "could not run" as "found nothing wrong", clearing every alert,
|
|
243
|
+
and telling you everything is fine at the moment you can see least.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Running it
|
|
248
|
+
|
|
249
|
+
Two commands, both dispatcher-driven, both safe to run by hand:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
celilo alerts sweep # timer.tick.5m — run due monitors, escalate, notify
|
|
253
|
+
celilo alerts poll # read replies off every bidirectional transport
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`poll` runs far more often than the sweep: "I texted ack and nothing happened"
|
|
257
|
+
is the experience the whole design exists to prevent.
|
|
258
|
+
|
|
259
|
+
celilo-mgr polls the transport; nothing calls in. That direction is
|
|
260
|
+
deliberate — an inbound webhook would need public HTTPS, which means caddy,
|
|
261
|
+
DNS and a certificate, so the ack path would depend on the very infrastructure
|
|
262
|
+
being paged about.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Answering deploy questions from your phone
|
|
267
|
+
|
|
268
|
+
Because an alert and an interview question are the same message with different
|
|
269
|
+
reply semantics, the same transport carries both. A headless deploy that needs
|
|
270
|
+
a value can ask you:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
❓ caddy needs: admin_email
|
|
274
|
+
|
|
275
|
+
reply K3M9QX <value>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Two rules, both enforced before anything is sent:
|
|
279
|
+
|
|
280
|
+
1. **A terminal wins.** If stdin is a TTY, the question is answered there — a
|
|
281
|
+
deploy started from your laptop must not appear to hang while silently
|
|
282
|
+
waiting for a text.
|
|
283
|
+
2. **Secrets are refused.** `secret.*` questions never travel over a messaging
|
|
284
|
+
transport, on a headless box or anywhere else. Typing a credential into a
|
|
285
|
+
chat puts it in the transport's message store and the device's history
|
|
286
|
+
before the responder ever sees it. Answer those at a terminal.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## See also
|
|
291
|
+
|
|
292
|
+
- **Rolling this out to a live fleet**: `openspec/changes/add-alerting/ROLLOUT.md`
|
|
293
|
+
— ordered stages, what to verify at each, and how to back out.
|
|
294
|
+
- **Building the libsignal aarch64 native** (needed on ARM hosts):
|
|
295
|
+
`modules/signal/build/README.md`
|
|
296
|
+
- Design and the decisions behind it: `openspec/changes/add-alerting/design.md`
|
|
297
|
+
- Subsystem map: [CELILO_SUBSYSTEMS.md](../CELILO_SUBSYSTEMS.md) § Alerting & notifications
|
|
298
|
+
- The Signal transport: `modules/signal/README.md`
|
package/docs/INDEX.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Celilo CLI Docs
|
|
2
|
+
|
|
3
|
+
Canonical user-facing docs live at https://celilo.computer/docs. This
|
|
4
|
+
file is the offline-readable summary for engineers working in the repo.
|
|
5
|
+
|
|
6
|
+
## Alerting
|
|
7
|
+
|
|
8
|
+
See [ALERTING.md](./ALERTING.md) — people, routes, escalation policies,
|
|
9
|
+
watching a module, and what to do when an alert arrives.
|
|
10
|
+
|
|
11
|
+
## System update runbook
|
|
12
|
+
|
|
13
|
+
Canonical: https://celilo.computer/docs/system-update
|
|
14
|
+
|
|
15
|
+
### Philosophy
|
|
16
|
+
|
|
17
|
+
`celilo system update` is the single command for bringing a deployment
|
|
18
|
+
back to "READY" — the audit-determined state where every drift category
|
|
19
|
+
is clean. It is built on three guarantees:
|
|
20
|
+
|
|
21
|
+
1. **Audit first.** `update` runs `system audit` as its first step and
|
|
22
|
+
refuses to proceed if the verdict is `BLOCKED`. The audit is also
|
|
23
|
+
exposed as a standalone read-only command (`system audit`) for use
|
|
24
|
+
in cron, CI, or interactive checks.
|
|
25
|
+
2. **Backups before mutation.** Every module that runs through the
|
|
26
|
+
update flow gets a fresh backup tagged with the run's `update_id`
|
|
27
|
+
before any change is applied. `--no-backup` skips this and is the
|
|
28
|
+
only way to opt out.
|
|
29
|
+
3. **Failures isolate to the dependency subtree.** The orchestrator
|
|
30
|
+
walks modules in topological order. A failure on a provider skips
|
|
31
|
+
only its transitive consumers — peers and unrelated modules continue.
|
|
32
|
+
Consumers may also proceed if their declared `requires` version
|
|
33
|
+
still satisfies the running provider (version-aware skip).
|
|
34
|
+
|
|
35
|
+
### Recommended sequence
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
celilo system audit # cheap, read-only — run any time
|
|
39
|
+
celilo system update --dry-run # preview the plan
|
|
40
|
+
celilo system update # do it
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For a single module:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
celilo system update --module <id>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For an audit that returns `BLOCKED` on a destructive Terraform plan:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
celilo system update --allow-destructive # only after reviewing the plan
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Rollback
|
|
56
|
+
|
|
57
|
+
Pre-update backups are tagged with the `update_id` printed in the
|
|
58
|
+
`system update` output. To roll back a single module:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
celilo backup list --module <id>
|
|
62
|
+
celilo backup restore <backup-id>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
System-state snapshots (the celilo SQLite DB) are taken before the
|
|
66
|
+
first module mutation in a run; restore them via the same flow with
|
|
67
|
+
the system-scoped backup id.
|
|
68
|
+
|
|
69
|
+
### FAQ
|
|
70
|
+
|
|
71
|
+
**Q. Why did `system update` skip module X?**
|
|
72
|
+
Likely one of:
|
|
73
|
+
- `system audit` reported no drift for X (already at latest).
|
|
74
|
+
- A provider that X depends on failed earlier in the run, AND X's
|
|
75
|
+
`requires` declares a version newer than the still-running provider.
|
|
76
|
+
Output line `skipReason` makes the cause explicit.
|
|
77
|
+
- `--module <id>` was passed and X wasn't the named module.
|
|
78
|
+
|
|
79
|
+
**Q. The audit says `capability_abi: BLOCKED` — what now?**
|
|
80
|
+
A module's manifest declares a capability version that the running
|
|
81
|
+
`@celilo/capabilities` SDK no longer supports (or vice versa). Either
|
|
82
|
+
upgrade the framework (`bun update -g @celilo/cli` then re-run `system
|
|
83
|
+
update`) or rebuild the module against the new SDK and republish.
|
|
84
|
+
`CAPABILITY_CONTRACT_VERSIONS` in `packages/capabilities/src/capability-contract.ts`
|
|
85
|
+
is the source of truth.
|
|
86
|
+
|
|
87
|
+
### Troubleshooting
|
|
88
|
+
|
|
89
|
+
**Schema migrations fail on update.** `system audit` checks pending
|
|
90
|
+
migrations as part of `schema` drift. If a migration is failing on
|
|
91
|
+
your DB, `bun run drizzle-kit migrate` from `apps/celilo/` will print
|
|
92
|
+
the error directly.
|
|
93
|
+
|
|
94
|
+
**Backups storage is full.** Pre-update backups live in their own
|
|
95
|
+
retention pool tagged with the `update_id`. They are pruned by
|
|
96
|
+
`backup retention` policy independently from regular backups. Inspect
|
|
97
|
+
with `celilo backup list --type pre-update`.
|
|
98
|
+
|
|
99
|
+
## Related
|
|
100
|
+
|
|
101
|
+
- Design doc: `apps/celilo/designs/CELILO_UPDATE.md`
|
|
102
|
+
- Audit categories: `apps/celilo/src/services/audit/`
|
|
103
|
+
- Orchestrator: `apps/celilo/src/services/update/`
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
CREATE TABLE `trusted_sources` (
|
|
2
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
3
|
+
`firewall_ip` text NOT NULL,
|
|
4
|
+
`subnet` text NOT NULL,
|
|
5
|
+
`description` text DEFAULT '' NOT NULL,
|
|
6
|
+
`registered_by` text NOT NULL,
|
|
7
|
+
`created_at` integer DEFAULT (unixepoch()) NOT NULL
|
|
8
|
+
);
|
|
9
|
+
--> statement-breakpoint
|
|
10
|
+
CREATE UNIQUE INDEX `trusted_sources_unique_idx` ON `trusted_sources` (`firewall_ip`,`subnet`);
|