An Agent Skill that teaches an agent to build a digital-signage module in a Next.js App Router app: ad media library, per-screen playlists, display registry, device pairing by PIN or provisioning URL, and a fullscreen TV player with health monitoring and remote control. Backend-agnostic between Firestore and Supabase/Postgres.
A screen in a venue is not a web page: it runs unattended for months on a cheap TV stick, nobody watches the console, and the failure mode is a frozen frame no one notices for a week. Every template is shaped around a player that cannot stop and a device that is never trusted.
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 has the record.
One command, via the skills.sh CLI, which installs the skill into every skills-compatible agent it detects, including Claude Code, Codex CLI and Gemini CLI:
npx skills add timerise-ai/digital-signageName the agents instead with -a, for example npx skills add timerise-ai/digital-signage -a claude-code -a codex.
Or clone it yourself. Nothing here is Claude-specific: the skill is a plain Agent
Skills folder, SKILL.md plus markdown references with no file that calls a model,
so cloning it into an agent's skills directory is all an install is. For Claude Code:
git clone https://github.com/timerise-ai/digital-signage.git ~/.claude/skills/digital-signageTo scope it to a single project instead, clone it into that project's .claude/skills/ directory. For another
agent, clone into that agent's skills directory, or symlink the Claude Code copy so one git pull updates
every agent:
mkdir -p ~/.agents/skills
ln -s ~/.claude/skills/digital-signage ~/.agents/skills/digital-signageUpdate the skill with git pull in its directory. The current release is 0.1.12. See
CHANGELOG.md. The skills index lists the other
Timerise Skills and how to install them all at once.
The skill activates automatically when a task matches its description: building in-venue screens, digital
menu boards or lobby displays, playlist scheduling, media upload with duration and orientation handling,
pairing a device by PIN or provisioning URL, screen health monitoring, remote screen control. Invoke it
explicitly with /digital-signage in Claude Code, $digital-signage in Codex CLI, or from /skills in
Gemini CLI.
Each host matches a task against the description its own way, so invoke the skill explicitly on a first run
rather than assuming it fired. Only SKILL.md is read up front; the references/ files load on demand, so
the skill stays cheap in context until a topic is actually needed.
| File | Contents |
|---|---|
SKILL.md |
Entry point: architecture diagram, critical facts, hard rules, quick start, and the reference directory |
README.md |
This front door |
CHANGELOG.md |
Keep a Changelog, one section per release, newest first |
CLAUDE.md |
What this repository is and the conventions for editing the skill itself |
LICENSE |
MIT |
references/adaptation.md |
The seam contract with the host app: the domain rename, the non-negotiables, what the host must already provide, integration points, order of work |
references/data-model.md |
Entities and types, the wire payload, array-vs-junction trade-off, active vs deletedAt, playback semantics, tenant scoping |
references/firestore-backend.md |
Security posture, lazy admin client, service module, client upload and server-side deletion, rules and composite indexes, Firestore traps |
references/supabase-backend.md |
Schema and migration, playlist resolution and reordering, RLS, storage, device-route services, optional Realtime |
references/api-routes.md |
Token minting and verification, pairing and playlist endpoints, admin routes, ETag caching, tests |
references/pairing.md |
Bootstrap and pairing, the hub screen, provisioning URL vs PIN |
references/player-runtime.md |
Kiosk chrome, the state machine, the player loop, crossfade, stall recovery, rotation, wake lock, error boundary, behaviour contract, offline |
references/admin-ui.md |
Upload-then-save, reading media metadata client-side, ad form and fit warning, media library, playlist editor, screen list and provisioning |
references/operations.md |
Heartbeat and health, preview, remote control (reload, blank, emergency takeover), audit log, unpairing |
references/extensions.md |
Dayparting, reusable playlists, proof of play, offline media precaching, multi-zone layouts |
references/provenance.md |
The engineering ledger: what the audit of the earlier implementation changed and how the templates verify it, what was kept on purpose, and what is new in the skill |
evals/ |
The prompts an operator types after installing (prompts.md) and one file per agent eval: the skill installed into an empty Next.js app, one prompt, no help, then type-checked, built and tested |
.github/workflows/agent-eval.yml |
Runs the agent evals on every published release through the index's reusable workflow; copied verbatim from the standard |
.github/cover.svg |
The cover at the top of this file: the signage motif from the skill's page on timerise.ai |
The backend seam runs through the reference set: api-routes.md and operations.md are written against
Firestore as the canonical backend, and supabase-backend.md defines every substitution: the junction table
that replaces display.adIds, and SQL equivalents of the service functions. The host app's auth, tenancy,
styling and i18n stay the host app's; adaptation.md is where you wire them in.
These travel with the module and are never optional. They are the hard rules in SKILL.md, in the same
order, and references/adaptation.md restates them.
- 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.mdandreferences/pairing.mdcarry 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.mdpins 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.mdcover 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.requireLocationScopeinreferences/api-routes.mdis called on every[id]route. activeis operator intent,deletedAtis 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.mddefines 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 inreferences/player-runtime.mdadvances 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.mdseparates the two channels.
Everything else is the host app's: domain names, backend, auth, tenancy, styling, i18n.
| Not this | Use instead |
|---|---|
| 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 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 |
Issues and pull requests are welcome here. Pure markdown, with no build, lint or test step: the TypeScript blocks describe the app the agent will generate, and each names its destination on its first line. Claims in this skill are meant to be verifiable: if you change a factual claim, say how you verified it, whether against the HTML media element and Screen Wake Lock specifications, the Firebase or Supabase documentation, Next.js, or a reproduction on a TV browser.
Adding, removing or renaming a file in references/ means updating the quick start and the reference
directory table in SKILL.md, the file table above, and any relative cross-links. Every odd-looking part of
the templates is there for a reason, and references/provenance.md is the ledger that must stay truthful:
read it before simplifying anything, and add an entry for anything you change. Commits follow Conventional
Commits and releases follow STANDARD.md in the
index; CLAUDE.md carries the full editing conventions.
This is one of the Timerise Skills: modules for Next.js App
Router apps written by our own senior engineers from the modules they have shipped, not synthetic, each
published as its own repository and indexed there. They share one layout, so an agent that has read one knows
how to read the next: a SKILL.md entry point, references/ loaded on demand, and a seam contract carrying
the module's non-negotiables.
Built and maintained by Timerise.
MIT. See LICENSE.