In-venue

Digital signage

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.

Released
September 21, 2026
npx skills add timerise-ai/digital-signage
v0.1.7
Current release
11
Reference docs
3
Non-negotiables
MIT
License

The problem

What problem does Digital signage solve?

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.

The module

What does the skill build?

An agent with this skill builds one module for a Next.js App Router app, on your stack. For Digital signage that module consists of:

  • 1. Media library
  • 2. Per-screen playlists
  • 3. Device pairing by PIN or URL
  • 4. Fullscreen TV player with stall recovery and health monitoring

Provenance

Where do the rules come from?

A screen in a venue is not a web page. It runs unattended for months on a cheap TV stick, nobody is watching the console, and the failure mode is a frozen frame no one notices for a week. 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.

Written by the engineer who has shipped this module. The earlier implementation it was audited against ran the screens of a multi-venue booking system; the architecture is that implementation's, the templates are not a transcription of it.

The ledger separates what the audit changed, what was kept on purpose, and what has not run in production yet.

  1. Fixed in the templates

    • The refresh timer that never fires The player fetched the playlist through a callback that closed over the current slide index, and the interval effect listed that callback in its dependencies. Every slide change tore down the interval and started a new one.
    • A side effect inside a state updater Playlist reconciliation ran inside a setAds(prev => …) updater, and called setCurrentIndex from within it. React may invoke an updater more than once for the same base state; a nested state write then fires twice.
    • active meaning two different things Soft delete set active: false — the same field the admin form's Active toggle writes — and the list query filtered on neither. A deleted ad appeared in the library as merely paused, and toggling it back on resurrected it.
    • A list cap that silently truncated playlists Playlist resolution fetched "the 200 newest ads" and filtered them by membership. Once a venue's library passed 200 items, older ads disappeared from playback with no error, no warning, and nothing in the admin UI to suggest it.
    • An unordered query behind a fallback A device polling without a screen id got "the location's first display" from a query with no orderBy. Which screen that was could change between deploys.
    Show 5 more
    • Tenant scope unchecked on [id] routes Collection routes derived the location from the staff session, but the [id] routes did not re-check it — so any admin could read, edit, or permanently delete another venue's ads and screens by id. A scope helper existed in the codebase and was used on two other modules; signage simply missed it.
    • Storage objects orphaned forever A delete helper was written, exported, and never called. Every permanent delete and every file replacement left its object in storage — up to 10 MB each, billed indefinitely, with no way to tell which were still referenced.
    • Dangling playlist references Removing an ad left its id in every playlist that referenced it. The player dropped unresolvable ids silently, so loops quietly got shorter.
    • One token for every screen, handed out for a PIN A single deployment-wide token lived in an environment variable. The pairing endpoint returned it verbatim to anyone who guessed a 4–6 digit venue PIN, with no rate limiting, and it granted access to every venue's playlist. Revoking one screen meant rotating the variable and re-pairing all of them.
    • No validation Request bodies were cast (as AdInput) with no runtime checking, and a helper existed purely to strip undefined values the database would reject. A schema validator was already a project dependency, used elsewhere.
Read the full record in provenance.md

Non-negotiables

What are the 3 rules the module never breaks?

Every module built from this skill holds these, whoever builds it. The same list is in the skill's README and SKILL.md, so the agent reads it before it writes a line.

  1. 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. Not a style preference; a compatibility requirement.

  2. Poll interval 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.

  3. 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.

Fit

When should you use it, and when not?

Use it for

  • Building or extending screens that loop media in a physical space, on Next.js App Router with Firestore or Supabase/Postgres.

Not for

  • Web ad serving: impressions, bidding, tracking pixels, third-party tagsInsteadAd-tech infrastructure; a different problem
  • Interactive kiosks where the user taps to transactInsteadThe sibling `booking-kiosk` skill; signage is one-way
  • A single embedded video on a marketing pageInsteadA <video> tag
  • Generic Next.js, CMS or auth setupInsteadThe host app; the skill assumes these exist

Build it yourself

How do I install it?

One command. The skills.sh CLI installs the skill into every skills-compatible agent it finds.

$ npx skills add timerise-ai/digital-signage

Claude Code

Invoke with /digital-signage

Codex CLI

Invoke with $digital-signage

Gemini CLI

Invoke with /skills

Name the agents instead with -a, for example npx skills add timerise-ai/digital-signage -a claude-code -a codex. Or clone the repository into your agent's skills folder. Nothing in it is agent-specific.

What is inside the repository (12 entries)
  • SKILL.mdEntry point: architecture diagram, critical facts, hard rules, quick start, and the reference directory
  • references/adaptation.mdThe 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.mdEntities and types, the wire payload, array-vs-junction trade-off, active vs deletedAt, playback semantics, tenant scoping
  • references/firestore-backend.mdSecurity posture, lazy admin client, service module, client upload and server-side deletion, rules and composite indexes, Firestore traps
  • references/supabase-backend.mdSchema and migration, playlist resolution and reordering, RLS, storage, device-route services, optional Realtime
  • references/api-routes.mdToken minting and verification, pairing and playlist endpoints, admin routes, ETag caching, tests
  • references/pairing.mdBootstrap and pairing, the hub screen, provisioning URL vs PIN
  • references/player-runtime.mdKiosk chrome, the state machine, the player loop, crossfade, stall recovery, rotation, wake lock, error boundary, behaviour contract, offline
  • references/admin-ui.mdUpload-then-save, client-side metadata extraction, ad form and fit warning, media library, playlist editor, screen list and provisioning
  • references/operations.mdHeartbeat and health, preview, remote control (reload, blank, emergency takeover), audit log, unpairing
  • references/extensions.mdDayparting, reusable playlists, proof of play, offline media precaching, multi-zone layouts
  • references/provenance.mdThe 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

Recent releases

  1. v0.1.7September 21, 2026

    Wording release. The skill content is unchanged from 0.1.6.

  2. v0.1.6September 2, 2026

    Wording release. Templates and technical content are unchanged from 0.1.5.

  3. v0.1.5September 2, 2026

    Wording release. The origin and audit statements across the skill follow section 2 of the skill standard; templates and technical content are unchanged from 0.1.4. The repository history starts at this release.

After installing

What do I tell my agent?

Say what you need in your own words; the skill supplies the how. These are starting points.

  1. 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.

    Supabase
  2. Our lobby screens should loop images and videos from Firestore, and a screen mounted in portrait must show the media the right way up.

    Firestore
  3. 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.

Build it with Timerise

How long does it take, and what does it cost?

We quote this module per project. The price depends on what it has to connect to. The path to a number is short and free:

  1. Step 1

    Brief

    Tell us what the module must connect to. Takes minutes, in a chat.

  2. Step 2

    Prototype in 48 hours

    A clickable prototype of your system and a quote, at no cost.

  3. Step 3

    Build and handoff

    One project price. Source code, documentation and IP are yours.

Two ways to get Digital signage

Build it yourself

Install the skill. Your own agent builds the module.

  • MIT licensed, no strings
  • Runs in Claude Code, Codex CLI and Gemini CLI
  • The same rules our engineers build by
$ npx skills add timerise-ai/digital-signage

Build it with Timerise

Send a brief. We build Digital signage into a system you own.

  • Clickable prototype and a quote within 48 hours, free
  • One project price, no subscription, no commission
  • Source code, documentation and IP handed over
3 years of support included.

Generated from the skill's own files at commit f8b36e2. Every rule above links to where the repository says it. All skills