Files
Signage/README.md
T
ekklesiaandClaude Opus 5.5 855b5f9f54
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 15s
CI / test (pull_request) Successful in 21s
CI / build (pull_request) Successful in 19s
CI / integration (pull_request) Successful in 6m0s
chore: release signage 0.1.2
0.1.2 fixes what the first production Signage deployment found:
- #4: the display and server share one message schema; every heartbeat
  from an image slide used to be refused with a 400;
- #5: every display request retries with backoff and never gives up, so
  a display that failed one request no longer stays offline until
  reloaded;
- #6: one object URL per cached slide, released when done; the leak
  exhausted the browser within minutes;
- CI (#7, #8): every change runs inside a pinned Ekklesia, end to end
  in a real browser, and cleans up even when killed.

compatibility.platform stays ^0.1.0: every fix is module-side, and CI
tested it against platform v0.1.1 plus test-only scripts. The README no
longer says Signage is unreleased or undeployed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 22:08:31 -05:00

142 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Signage
Ekklesia's visual programming, timeline, synchronization, and display-delivery module.
Signage decides **what is on a screen, when, and on which screens together**. It natively supports
static images; every other kind of content reaches a screen because another Ekklesia module
registered a content provider and contributed a live React renderer for it. Signage never learns what
an announcement, a Bible passage, or a trivia question is.
This repository is a standalone, independently versioned Ekklesia module package. It is not a fork of
Ekklesia and vendors none of its SDKs.
- Module id: `signage`
- Package: `@ekklesia/signage`
- Manifest version: 7
## Status
Version 0.1.2 is feature-complete for its declared scope, and Signage has run in production since
0.1.1. What exists today:
- The module package, manifest (v7), four capabilities, six permissions, ten operator settings, and
one logical storage bucket.
- Eleven tables and a single initial migration, declared non-destructive.
- Native image ingest: one image per streamed request, 25 MiB, content-addressed, with display and
thumbnail renditions and temporary object keys removed after any unsuccessful upload.
- The content provider contract and registry, a bounded and sanitized call path, and Signage's own
built-in image provider registered through that same capability.
- Timelines, provider source slots, pinned slots, channel groups, synchronized audience channels,
authoritative playback with absolute instants, activation and scheduling, and viewer
registration, assignment, heartbeat, and health.
- A management-UI artifact and a full-viewport display artifact, both built through the canonical
Ekklesia module UI pipeline and mounted through the platform's browser ABI.
- Two permanent scene layers with a crossfade that never fades the outgoing slide, provider
renderers mounted as browser contributions, the built-in "Content unavailable" slate, deliberate
blackout, and offline continuation from this display's own persisted state.
No cutover from the legacy Announcements container has happened or is planned by this repository.
Video, audio, MQTT, Home Assistant, Companion, and a notification outbox are out of scope; so is
any content module — Signage ships with no announcements, scripture, or trivia, and cannot render
any until one is installed.
### Offline cold start is not yet possible
A display that is **already mounted** keeps playing with no network: its schedule, playback state,
provider payloads, and media are Signage's own, persisted by this module in IndexedDB and Cache
Storage. Core's service worker caches module *code*, not Signage domain data.
A display that is **restarted while Ekklesia is completely unreachable** cannot mount Signage at all.
Mounting requires an authenticated module descriptor, and Core does not yet cache one. True offline
cold start depends on a Core-provided authorized offline display descriptor or lease; until that
exists, this module does not claim it. See ADR 0006.
## Developing against Ekklesia
Signage is developed **inside the authoritative Ekklesia workspace**, checked out at the path it will
eventually be installed at:
```bash
git clone <ekklesia> ekklesia
git clone <this repository> ekklesia/modules/signage
cd ekklesia
pnpm install
```
`modules/*` is an Ekklesia workspace glob, so the checkout is picked up with no registry edit,
no second copy of Ekklesia, and no vendored SDK. `@ekklesia/module-sdk`, `@ekklesia/module-ui-sdk`,
`@ekklesia/design-tokens`, `@ekklesia/ui`, and `@ekklesia/module-ui-build` all resolve through the
workspace as `workspace:*` dependencies.
Two consequences worth knowing:
- Ekklesia uses a shared workspace lockfile, so installing here writes an importer entry for
`modules/signage` into Ekklesia's `pnpm-lock.yaml`. That entry belongs to whoever maintains the
Ekklesia checkout; it is not committed from this repository.
- Signage requires the Ekklesia platform prerequisites for shared design tokens, module stylesheets,
browser contributions, bounded streaming request bodies, and display offline code
(ADRs 0039–0043). An Ekklesia checkout without them cannot build or validate this module.
### Commands
Run from the **Ekklesia repository root**:
```bash
pnpm --filter @ekklesia/signage build # tsc + the canonical module UI build
pnpm --filter @ekklesia/signage test # unit and browser-artifact tests
pnpm --filter @ekklesia/signage typecheck # node, browser, and browser-test projects
pnpm --filter @ekklesia/signage lint
pnpm boundaries # four-layer boundary enforcement
pnpm modules:validate # static manifest, entrypoint, and schema validation
pnpm migrations:validate # migration journal, policy, ordering, and checksums
pnpm verify # the full Docker-free gate
```
Signage's tests are its own: `pnpm test` here runs `vitest` against this package's `vitest.config.ts`,
which includes `.test.tsx` as well as `.test.ts`. The workspace-root config includes only `.test.ts`,
so a browser test added here without that config would be silently skipped.
Never run `pnpm migrations:apply` against a persistent database while developing this module. Signage
reads no environment variable, constructs no database client, configures no object storage, hardcodes
no Ekklesia URL, opens no socket of its own, and applies no migration: every one of those reaches it
through the host.
### Changing the schema
```bash
pnpm --filter @ekklesia/signage migration:generate # drizzle-kit, into drizzle/
```
`tests/schema.test.ts` pins the checksum of the generated SQL, so a regenerated migration fails the
suite until the pin is updated deliberately. `drizzle/meta/_ekklesia.json` declares the migration
non-destructive; `pnpm migrations:validate` enforces that.
### Verifying what only a browser can show
Automated DOM assertions cannot prove a crossfade, a letterboxed image, or a slate. Those are
verified by driving the **built** artifacts in a real Chromium against a stub host and reading the
frames: native fill, both letterboxed fits, a contributed renderer, a frozen mid-crossfade composite,
the slate, a blackout, the offline indicator, every management tab, and the narrow-screen gate. The
harness is deliberately not committed — it stubs a host and would rot into a second, wrong
description of the platform ABI.
## What Signage owns, and what providers own
| Signage owns | A content provider owns |
| --- | --- |
| Native image assets and their renditions | Its own domain content and tables |
| Saved timelines, repeating patterns, and slots | Which of its content is available now |
| Channel groups and synchronized audience channels | Selecting the next item from one of its sources |
| Slot timing and authoritative playback | Repeat and no-repeat policy |
| Timeline activation and scheduling | Audience-specific variants and fallback |
| Viewer registration, assignment, heartbeat, health | Its own live React presentation |
| Transitions, offline continuation, playback controls | Dynamic change within one slide |
| The management and display UI | Its own internal multi-stage behaviour |
## Licence and scope
Deployment, the module store, a theme editor, a mobile management UI, video and audio, MQTT, Home
Assistant, Companion, and any migration of legacy Announcements data are explicitly out of scope for
this repository.