2026-05-20 15:13:31 +10:00
2026-08-22 21:49:56 +10:00
2026-05-20 14:57:01 +10:00

Scratching Post — Sunbeam Digital Signage

A self-hosted digital notice board / signage system built on ASP.NET Core 8, designed for IIS on Windows Server 2019/2022.

Terminology

Component Name Description
Individual slide Meow A single content slide — WYSIWYG, embed, or calendar
Slide collection PURR Persistent User Requests and Reminders — a device playlist
Priority slide ASK Attention Seeking Kitty — overrides collection order (planned)
Display node Kitten A device that plays a playlist of Meows
Backend Scratching Post The admin panel where all content is managed
Framework Sunbeam The underlying system powering everything

Features

  • WYSIWYG Editor — TinyMCE with image upload, tables, rich text
  • Embed Web Content — iframe any URL as a slide
  • ICS Calendar — display upcoming events from any .ics feed
  • Device Playlists — assign slides to specific devices with individual durations
  • Per-Device Resolution — configure display resolution per device
  • URL-Based Devices — access displays via http://server/frontofhouse, http://server/staffroom
  • Drag-and-Drop Playlist — reorder slides in the admin
  • Live Updates — display polls for changes every 30 seconds
  • Full-Screen Display — hidden cursor, clock overlay, progress bar
  • Smooth Transitions — fade or slide transitions
  • Meow Expiration — give any slide an expiry date/time; it's automatically skipped on displays afterwards (and stays in the library, unedited, in case you need it again)
  • Docker Support — run via docker compose up as an alternative to IIS, with volumes for the database and uploads

Requirements

IIS (Windows Server):

  • Windows Server 2019 or 2022
  • IIS with ASP.NET Core Hosting Bundle
  • .NET 8 SDK (for building) / .NET 8 Runtime (for hosting)

Docker (any host):

Quick Start

1. Install Prerequisites

Download and install:

2. Build

cd ScratchingPost
dotnet restore
dotnet publish -c Release -o ./publish

3. Deploy to IIS

  1. Open IIS Manager
  2. Create a new Site (e.g. ScratchingPost)
  3. Set the physical path to the publish folder
  4. Set the binding (e.g. port 80, or a hostname like sign.local)
  5. Set the Application Pool to No Managed Code (the Hosting Bundle handles .NET)
  6. Ensure the App Pool identity has write access to the publish folder (for SQLite DB and uploads)

4. First Run

The SQLite database (noticeboard.db) is created automatically on first start with two demo devices seeded:

  • Front of House at http://server/frontofhouse
  • Staff Room at http://server/staffroom

5. Access

URL Purpose
http://server/admin Scratching Post admin panel
http://server/admin/slides Manage Meows (slides)
http://server/admin/devices Manage Kittens (devices)
http://server/frontofhouse Front of House display
http://server/staffroom Staff Room display

Docker Deployment

An alternative to IIS — runs anywhere Docker does (Linux, Windows with WSL2 backend, Synology/QNAP NAS, a Raspberry Pi, etc.).

1. Build and run

git clone https://gitea.hideawaygaming.com.au/jessikitty/ScratchingPost.git
cd ScratchingPost
docker compose up -d --build

That's it — the container listens on port 8080, so open http://server:8080.

2. What the compose file sets up

docker-compose.yml builds the image from the included Dockerfile and wires up two bind-mounted folders next to the compose file so your data survives container rebuilds/updates:

Host path Container path Purpose
./data /data noticeboard.db (SQLite)
./uploads /app/wwwroot/uploads Images uploaded via TinyMCE, plus uploaded ICS files

Both folders are created automatically on first run.

3. Configuration

Same settings as IIS, just as environment variables in docker-compose.yml (nested keys use a double underscore):

environment:
  - DataDirectory=/data
  - TimeZone=UTC
  - Admin__Username=admin
  - Admin__Password=ScratchingPost2026!
  - MaxUploadSizeMB=20

Change the admin password before exposing this beyond your local network.

4. Updating

git pull
docker compose up -d --build

The database and uploads persist across rebuilds since they live in the bind-mounted ./data and ./uploads folders, not inside the image. On startup the app checks the SQLite schema and adds any new columns it needs (e.g. the ExpiresAt column below) automatically — no manual migration step.

5. Reverse proxy / HTTPS

The container serves plain HTTP on 8080. For a public-facing deployment, put it behind a reverse proxy (Caddy, nginx, Traefik) that terminates HTTPS and forwards to scratchingpost:8080.

Running IIS and Docker side by side

The IIS deployment and the Docker image are built from the same codebase and don't share a database or uploads folder by default — they're independent instances unless you point them at the same SQLite file and uploads directory yourself.

Configuration

Edit appsettings.json:

{
  "MaxUploadSizeMB": 20
}

How It Works

  1. Create Meows (slides) — use the WYSIWYG editor for rich content, embed external URLs, or point to an ICS calendar feed
  2. Create Kittens (devices) — give each a name, URL slug, and display resolution
  3. Build PURRs (playlists) — assign Meows to Kittens with individual durations and drag-and-drop ordering
  4. Display — point a browser at http://server/{slug} and it runs full-screen with smooth transitions

The display page polls for changes every 30 seconds, so updates appear on devices without restarting.

Meow Expiration

Any Meow can be given an Expires date/time in its editor (under Scheduling). Once that time passes:

  • The Meow is automatically skipped by every Kitten it's assigned to — no manual removal needed
  • It stays fully intact in Meows (with a red Expired badge) so it can be re-used or have its expiry pushed back later
  • Nothing is deleted — expiry only affects playback, not the stored slide

Leave it blank for a Meow that should run indefinitely.

Time zone: expiry is checked against the TimeZone setting, not the raw OS clock:

  • IISappsettings.json ships with "TimeZone": "", which falls back to the Windows Server's own local time zone (the original behaviour — no change needed on upgrade).
  • Dockerdocker-compose.yml sets TimeZone=UTC by default, since containers are otherwise timezone-ambiguous. Override it to an IANA id (e.g. Australia/Melbourne) if you'd rather enter expiry times in local time than UTC; the image includes tzdata so real zone names resolve correctly.

Display Controls

Keyboard shortcuts on display pages (useful during setup):

Key Action
Right Arrow or Space Skip to next slide
Left Arrow Go to previous slide
F or F11 Toggle fullscreen
C Toggle cursor visibility

Data Storage

  • Database: SQLite (noticeboard.db) — created automatically, zero config
  • Uploads: wwwroot/uploads/ — images uploaded via TinyMCE or the file upload API
  • On IIS, both need write permissions for the App Pool identity
  • On Docker, both live in bind-mounted host folders (./data, ./uploads) — see Docker Deployment

TinyMCE Note

The WYSIWYG editor uses TinyMCE CDN with no-api-key. This shows a small notification banner. For production, register for a free TinyMCE API key at tiny.cloud and replace no-api-key in Views/Slides/Create.cshtml and Edit.cshtml.

Project Structure

ScratchingPost/
├── Controllers/         # MVC controllers
│   ├── AdminController   # Dashboard
│   ├── SlidesController  # Meow CRUD
│   ├── DevicesController # Kitten CRUD + Playlist management
│   ├── DisplayController # Device display + playlist API
│   └── ApiController     # Image upload, ICS parsing
├── Data/                # EF Core DbContext + seed data
├── Infrastructure/      # Clock (time-zone-aware "now" for Meow expiry)
├── Models/              # Slide, Device, DeviceSlide
├── Routing/             # DeviceSlugConstraint
├── Views/               # Razor views (Admin, Slides, Devices, Display)
├── wwwroot/             # Static files
│   ├── css/             # admin.css, display.css
│   ├── js/              # admin.js, display.js (Sunbeam engine)
│   └── uploads/         # User-uploaded images
├── appsettings.json     # Configuration
├── web.config           # IIS configuration
├── Dockerfile           # Container build (alternative to IIS)
├── docker-compose.yml   # Container run config (ports, volumes)
├── .dockerignore
└── NoticeBoard.csproj   # Project file

Version History

  • v1.1.1 — Meow expiry now checked against a configurable TimeZone setting instead of the raw OS clock: IIS keeps its existing local-time behaviour by default, Docker defaults to UTC (overridable to any IANA zone; the image ships with tzdata)
  • v1.1.0 — Docker deployment (Dockerfile + docker-compose.yml, configurable data directory, volume-backed DB/uploads), Meow expiration (ExpiresAt field, automatic playback filtering, auto-migrating column for existing databases)
  • v1.0.0 — Initial release: WYSIWYG slides, embed, ICS calendar, device playlists, drag-and-drop ordering, live polling, IIS-ready deployment
S
Description
Sunbeam Digital Signage — A self-hosted notice board system for IIS. WYSIWYG slides, embeds, ICS calendars, device playlists.
Readme
1.1 MiB
2026-05-21 08:59:02 +10:00
Languages
JavaScript 82.6%
HTML 10.2%
C# 5%
CSS 2%
Dockerfile 0.2%