← back to the rink

What is Rink?

Rink is the easiest way to put something on the web at SeenThis. You make a little website — even just one page — and it goes live in seconds at its own address, visible only to people at SeenThis. No setup, no waiting on anyone.

Great for a prototype, a campaign mockup, an internal tool, a live poll, a one-off page for a meeting — anything you want to show people instead of just describe.

Ways to drop the puck

  1. Drag & drop

    Have a folder with an index.html? Drop it on the front page — live in about two seconds.

  2. Open the Locker Room

    Open a site's Locker Room, change files with syntax highlighting, preview the draft, then publish when it looks right. No local setup, no build step. The Locker Room also has a built-in AI chat — describe a change in plain language and Claude rewrites your draft files for you (it runs on your personal AI credit — see below).

  3. Ask an AI to build it

    Don't want to make the files yourself? Tell Claude or ChatGPT what you want — it builds the page and puts it live on Rink.

  4. Let your AI deploy for you (MCP)

    Connect Rink to Claude or ChatGPT once, then just say "deploy this folder to Rink" — the assistant ships, lists and rolls back your sites itself. How to connect →

  5. From the terminal

    Developers: salming rink deploy from any folder. Rollback, preview and share — all from the CLI. Full command reference →

Who can see it?

By default, everyone at SeenThis (and only SeenThis — it's behind the company login). On any site you own you can also:

Locker Room, drafts and versions

Every site has a Locker Room for quick changes after it is live. The Locker Room works on a draft, which is separate from what visitors see. Your file changes autosave to the draft, the preview shows that draft, and the live site only changes when you press Publish.

AI credit

Anything that calls a paid AI model costs real money per request, so each person has a small AI credit balance (measured in actual USD cost). Every AI action shows what it cost and how much you have left.

What a Rink site can do

Plain web pages are often enough. But when you need a bit more, it's already there — no accounts, no API keys, no setup:

Save data — store and read entries (votes, posts, sign-ups).
Upload files — accept images, PDFs & attachments; stored per site.
Use AI — text, images (Nano Banana) & video (Veo) from the page; no key.
Know the viewer — who's looking (SeenThis email, name & Slack).
Find colleagues — look up any teammate's email & Slack handle.
Send email — notify a SeenThis colleague.
Schedule — run something daily/weekly automatically.
Realtime — live updates for everyone at once (polls, cursors).

If you're using an AI to build your site, just ask for these by name — it knows how to wire them up.

Drive Rink from your AI assistant (MCP)

Rink speaks MCP, so you can hook it straight into Claude or ChatGPT as a connector. Once it's connected the assistant can deploy a folder, list your sites and roll one back — you just ask in plain language, no CLI, no copy-pasting.

  1. Add the connector — once

    In Claude (Settings → Connectors → Add custom connector) or ChatGPT (Settings → Connectors), paste the Rink MCP URL:

    https://mcp-rink.seenthis.io/mcp
  2. Sign in with SeenThis

    The first time, it opens a SeenThis Google login (the same one that guards Rink). Approve it and the assistant is linked to your account — it only ever touches sites you're allowed to. Needs the VPN, like everything else on Rink.

  3. Just ask

    "Deploy the ./landing folder to Rink as q3-launch", "list my Rink sites", or "roll q3-launch back to the previous version." Under the hood it calls rink_deploy, rink_list and rink_rollback.

Under the hood (for the curious)

site-a.rink… site-b.rink… site-c.rink… nginx-ingress VPN + Google SSO rink-server one service Static files · S3 sites/<slug>/<deploy>/ versioned · instant rollback in-memory cache rink.* APIs · /_rink/* rink.db — Postgresrink.ai — Geminirink.identity — SSOrink.files — S3rink.email — SMTPrink.schedule · rink.live GET /* /_rink/*
How Rink sits on top of Salming — the specifics

Rink is a single ordinary Salming app — it inherits the whole platform instead of running any infrastructure of its own. Each capability maps to a Salming building block:

One service, many hostsrink-server (Fastify, the @seenthis-ab/fastify-api platform package) is deployed by the standard salming-deploy workflow onto the shared EKS cluster, with Kustomize overlays per environment. It dispatches on Host: the apex is the control plane, {site}.rink… serves a site, api.rink… takes CLI tokens, mcp-rink… is the MCP.
Storage & data — site files live in the Salming-provisioned S3 bucket (ST_BUCKET), versioned per deploy under sites/<slug>/<deploy>/, with Locker Room drafts under sites/<slug>/draft/. Metadata, draft state, rink.db documents, analytics and jobs live in the Salming RDS Postgres (ST_DATABASE) — no per-site database.
Access & identity — the internal ingress is gated by google-login-platform (oauth2-proxy); Rink reads the X-Auth-Request-Email header rather than running its own login. The VPN is the network door; per-site sharing is layered on top in Postgres.
Routing & certsexternal-dns publishes the wildcard records and cert-manager issues the TLS certs. Internal sites resolve on *.rink.<env>.seenthis.io (internal NLB); public ones go out through Fastly on *.rink.<env>.seenthis.se.
Security review — when a site is published publicly, Rink queues a read-only Gunvald review for the active artifact and stores the result on the deploy. Passing reviews, and warnings where Gunvald inspected the artifact, can advance the public pointer; blocked or incomplete findings keep the public host on the last reviewed version.
AI, email & secrets — the Locker Room's AI code editing runs on Anthropic (Claude, via forced tool-use) and draws down a per-user USD credit; rink.ai for sites runs on Gemini (text, images, video); rink.email sends over the shared SMTP. Every key is delivered as a SOPS + KMS-encrypted secret through the same Salming secret pipeline every app uses.
Realtime & jobsrink.live fans out across pods via Postgres LISTEN/NOTIFY, and rink.schedule runs as an in-process cron loop — both ride the platform's existing observability (logs, metrics, traces) with no extra moving parts.

Net: adding a feature to Rink is usually just calling another Salming primitive — not standing up new infrastructure.

For developers

Everything above is one script tag away. Skip this section if you're not writing code.

Add one line and a global rink appears. It talks to your site's own address, so the logged-in SeenThis user and your site's data are scoped automatically — no keys, no config.

<script src="/rink.js"></script>

rink.db — a schemaless store, Firebase-style. Each site has its own collections; no schemas, no migrations.

const posts = rink.db.collection('posts');
await posts.create({ title: 'Lunch vote', spot: 'Tacos' });
const all = await posts.list({ limit: 50 });
await posts.update(id, { spot: 'Sushi' });
await posts.remove(id);

rink.files — upload files (images, PDFs, attachments). Bytes go to S3 per site; you get back a URL you can drop straight into the page.

const f = await rink.files.upload(input.files[0]);  // from an <input type="file">
img.src = f.url;                       // or rink.files.url(f.id)
const all = await rink.files.list();   // [{ id, name, size, type, url, … }]
await rink.files.remove(f.id);

rink.live — realtime. Subscribe to a collection and every connected visitor updates the instant data changes; or use raw channels for things like multiplayer cursors.

// live data — fires on every create/update/delete, for everyone
posts.subscribe({
  onCreate: (doc) => addRow(doc),
  onUpdate: (doc) => updateRow(doc),
  onDelete: (id) => removeRow(id),
});

// raw pub/sub channel
const ch = rink.live.channel('cursors');
ch.on((msg) => draw(msg));
ch.send({ x, y });

rink.ai — text, images and video from the browser. Keys live on the server; you never handle them. Text defaults to Claude Sonnet — pass model: 'gemini' for Gemini.

const reply = await rink.ai.chat('Summarise these notes…');
const reply = await rink.ai.chat(messages, { model: 'gemini' });  // or 'haiku', 'opus'

// image (Nano Banana) — saved to rink.files, returns the file
const img = await rink.ai.image('a flat-vector hockey puck logo');
icon.src = img.url;

// video (Veo) — long-running; resolves with the file when ready
const vid = await rink.ai.video('slow pan over a frozen lake',
                                { onProgress: (s) => show(s.status) });
player.src = vid.url;

rink.identity — who's looking, straight from SSO. Enriched with their real name and Slack handle from the directory. No login code to write.

const me = await rink.identity.me();  // { email, user, name, slack }

rink.directory — look up colleagues: real name, email and Slack handle (synced from Slack). Great for "@mention" pickers or share UIs.

const people = await rink.directory.list();        // [{ email, name, slack }]
const p = await rink.directory.lookup('billy@seenthis.se');  // { email, name, slack }

rink.email — send mail to a SeenThis address (recipients are restricted to @seenthis, so it can't be used as a spam relay).

await rink.email.send({
  to: 'team@seenthis.se',
  subject: 'Lunch poll closes at 11',
  text: 'Vote now: …',
});

rink.schedule — run something on a schedule. The action can call a webhook, run an AI prompt into a collection, or send an email.

await rink.schedule.add({
  schedule: '0 9 * * 1-5',        // weekdays 09:00 (cron syntax)
  action: { type: 'ai',
            prompt: 'Summarise yesterday\'s notes',
            collection: 'daily_digest' },
});
// also: {type:'webhook',url} · {type:'email',to,subject,text}

Calls are scoped to the site + signed-in user automatically. Limits keep the shared platform healthy: 50k docs & 256 KB per document, per-person AI & email rate limits, 10 scheduled jobs per site. Public (.se) sites are static-only — the rink.* APIs are internal-only by design.

Command line

Prefer the terminal? Install the Salming CLI (npm i -g @seenthis-ab/salming) and deploy any folder. login is a one-time browser step that mints a token.

salming rink login                  # one-time; opens a browser, paste the token back
salming rink deploy                 # tar the current folder, upload, print the live URL (~2s)
salming rink deploy --site q3-launch # choose the subdomain (default: folder name)
salming rink rollback               # instant rollback to the previous deploy
salming rink deploys                # deploy history for this site
salming rink list                   # all my sites
salming rink share a@seenthis.se    # make private to specific colleagues (emails them)

Same engine as drag-and-drop and the MCP connector — a site is always just files + a draft + version pointers.

Need a real, permanent product?

Rink is for quick internal things. When something needs its own database, custom domain, real CI/CD and monitoring, build it on Salming, SeenThis's full application platform. Outgrew Rink? The infra team will help you graduate a site into a proper Salming app. Explore Salming →

Inspiration

Rink stands on two shoulders:

Shopify Quick → Virke Sites →

Built by the infra team. Questions, quota bumps, ideas — email infra@seenthis.co or ping #product-team-infrastructure on Slack.