# Digital signage (Agent Skill)

> Screens in your venue: a media library, playlists per screen, device pairing by PIN, and a fullscreen TV player that recovers from stalls and reports its health.

Page: https://timerise.ai/skills/digital-signage

Agent Skill `digital-signage`: https://github.com/timerise-ai/digital-signage. Category: In-venue. Runs on Next.js; backend: Firestore or Supabase.

Latest release: v0.1.12 (2026-10-08).

Install: npx skills add timerise-ai/digital-signage. Timerise builds it on a brief; the price comes from the brief.

Screens in a venue look simple: put a playlist on a TV. They fail in ways nobody notices. A video stalls. A portrait screen shows a rotated frame. A TV stick reboots and forgets which screen it is. The frozen frame stays for a week because nobody watches a console.

This skill builds the whole signage module: a media library, playlists per screen, a registry of displays, pairing by PIN or provisioning URL, and a fullscreen player written to run unattended for months on a cheap TV stick. The player recovers from stalls, rotates for portrait screens and reports whether it is alive.

It runs on Firestore or Supabase, inside your admin app. Each screen has its own token and can only read its own playlist.

Build a digital-signage module in a Next.js App Router app: an ad media library, per-screen playlists, a display registry, device pairing and a fullscreen TV player. Use when: (1) building or extending in-venue screens, digital menu boards, lobby or waiting-room displays, or an advertising-slot system, (2) implementing playlist scheduling, media upload with duration and orientation handling, device pairing by PIN or provisioning URL, screen health monitoring, or remote screen control, (3) auditing a player that freezes, never refreshes or shows portrait media sideways, (4) the user mentions: digital signage, display screens, TV player, ad playlist, signage module, kiosk display, screen management, proof of play, /api/display/pair, /api/display/playlist, ETag, 304 Not Modified, SIGNAGE_TOKEN_PEPPER, wake lock, video stall, firebase-admin, Supabase RLS. Carries player internals that recover from video stalls, rotate for portrait screens and survive TV-browser quirks, and a data and auth model with hashed per-device tokens and venue-scoped playlists, each pinned by a behaviour contract or a route test. Next.js App Router; the data layer is a seam, with Firestore and Supabase/Postgres both carried. Not web ad serving and not an interactive kiosk.

This skill was written by the engineer who has shipped this signage module; the earlier implementation it was audited against ran the screens of a multi-venue booking system. The templates hold the properties that keep a screen alive unattended: a player that skips a stalled video or a broken asset on its own and keeps looping through an outage, a poll that fires on a stable interval whatever the slide state, a per-device token stored hashed and revoked with one row update, and every playlist and route scoped to its venue server-side. The player's behaviour contract and the route tests state each one; [`references/provenance.md`](references/provenance.md) has the record.

What it builds:
- Media library
- Per-screen playlists
- Device pairing by PIN or URL
- Fullscreen TV player with stall recovery and health monitoring

When to use:
- Building or extending screens that loop media in a physical space, on Next.js App Router with Firestore or Supabase/Postgres.

When not to use:
- **Web ad serving** (impressions, bidding, tracking pixels, third-party tags): a different problem with different infrastructure.
- **Interactive kiosks** where the user taps to transact. Signage is one-way.
- **A single embedded video** on a marketing page. Use a `<video>` tag.
- Generic Next.js, CMS, or auth setup, which is assumed to exist.

Non-negotiables:
- Inline styles on device components. Signage runs on TV browsers years behind current, so the player cannot depend on the host app's CSS pipeline, and a stylesheet or purge step cannot break a screen nobody is watching. The device components in `references/player-runtime.md` and `references/pairing.md` carry no class names.
- The poll interval is independent of render state. The poll runs on a stable timer and reads slide state from a ref, so a refresh longer than a slide still fires on time. The behaviour contract in `references/player-runtime.md` pins the poll's timing and failure handling.
- Per-display tokens, stored hashed. Each screen holds its own token, the server stores only the hash, and revocation is one row update; the device is untrusted hardware anyone can walk up to. The route tests in `references/api-routes.md` cover a revoked token.
- Tenant scope is derived server-side and enforced on every `[id]` route, answering 404, not 403. A client-supplied scope is not a scope, whether a header, a cookie or a query parameter carries it, and a 403 tells a prober the id exists. `requireLocationScope` in `references/api-routes.md` is called on every `[id]` route.
- `active` is operator intent, `deletedAt` is lifecycle. One boolean for both lets a toggle bring a deleted item back; two fields give the admin three states, Active, Paused and Deleted. `references/data-model.md` defines both.
- An image's duration timer starts at `onLoad`. On a slow TV a timer started at mount expires before the slide is visible. The player machine in `references/player-runtime.md` advances only after the slide reports ready.
- Durable screen state is `mode`, delivered on every poll, and a reload persists its ack before reloading. Blank and takeover must survive the daily reload and a power cycle, and an ack lost in the reload is re-delivered forever. `references/operations.md` separates the two channels.

Not this:
- Web ad serving: impressions, bidding, tracking pixels, third-party tags: Ad-tech infrastructure; a different problem
- Interactive kiosks where the user taps to transact: The sibling [`booking-kiosk`](https://github.com/timerise-ai/booking-kiosk) skill; signage is one-way
- A single embedded video on a marketing page: A `<video>` tag
- Generic Next.js, CMS or auth setup: The host app; the skill assumes these exist

Booking features we build with it:
- Self-service kiosk and check-in: /features/kiosk-and-check-in

What to tell your agent:
- Build digital signage for our venue on Supabase: a media library, a playlist per screen, pairing a TV by PIN, and a fullscreen player page the TV opens.
- Our lobby screens should loop images and videos from Firestore, and a screen mounted in portrait must show the media the right way up.
- Our TV player freezes on a video and never recovers. Add stall recovery and a health signal so we can see which screen is down.

Agent eval results (newest per agent and prompt):
- Gemini CLI (gemini-3.8-flash) on v0.1.12, 2026-10-07: pass (Supabase)
- Codex CLI (gpt-6.1-sol) on v0.1.12, 2026-10-07: pass (Supabase)
- Claude Code (claude-opus-5-5) on v0.1.12, 2026-10-07: pass (Supabase)

Recent releases:
- v0.1.12 (2026-10-08): Security fix release, from scoring the prompt-1 agent eval runs against 0.1.11. Each defect was found by an agent's edit and reproduced against the templates before the fix was adopted.
- v0.1.11 (2026-10-08): Security fix release, from scoring the prompt-1 agent eval runs against 0.1.10. Each defect was found by an agent's edit and reproduced against the templates before the fix was adopted.
- v0.1.10 (2026-10-07): Fix release, from scoring the prompt-1 agent eval runs against 0.1.9.
