v0.1 — scaffold
Express + better-sqlite3 + Vite + JWT, multi-stage Docker, GH Actions,
bootstrap admin/admin, host CRUD, branding settings,
configurable visitor-form schema (config/visitor-form.json).
Shipped.
visitor sign-in · for the workshop's actual front door
A small, honest visitor sign-in app built for a VFX workshop — not for a 50-floor office tower, not for a security-theater corporate lobby, not for a SaaS that wants to license your reception desk.
visitas.world (the name is from the Spanish noun visitas — “visits”) is a self-hosted visitor sign-in app. An iPad parked at reception runs the kiosk, a visitor taps in (name, company, host, purpose), the host gets pinged, an AirPrint badge prints, and an active-visitors view tells you who's on-site if the fire alarm goes off.
It runs as a single container next to your other infrastructure. There's
no SaaS dependency, no “talk to sales”, no per-seat licensing. The data
file lives on your disk; you can scp it.
Sister project to cambiar.world — same workshop, same stack, same philosophy.
visitas-world. The bootstrap admin keeps you unlocked even if AD is down.
Shipped 2026-05-06 as 1.0.0: scaffold, kiosk sign-in,
active visitors + wall view, security role, host notifications,
multi-iPad with AirPrint badges, NDA + safety acknowledgments with
drawn signature, returning-visitor pre-fill with 1-year NDA cache,
pre-registration via QR, opt-in photo capture with 30-day retention,
and Active Directory host lookup. Each card below was its own minor
release on the v0 track.
Express + better-sqlite3 + Vite + JWT, multi-stage Docker, GH Actions,
bootstrap admin/admin, host CRUD, branding settings,
configurable visitor-form schema (config/visitor-form.json).
Shipped.
The iPad-facing sign-in flow: visitor types name / company / host /
purpose, active-visitors admin page, public wall view at /active
for fire drills, sign-out at the same kiosk. Full audit log on every
transition. Adds a second security role for reception staff.
Shipped.
Email via SMTP (nodemailer) and SMS via a small Twilio
REST adapter. Per-channel events filter; secrets in env, transport
config in config/notifications.json. Settings page test
buttons for both channels.
Shipped.
Each entrance gets its own kiosk URL (/kiosk/<slug>)
with a default-printer-name hint (MDM enforces the actual AirPrint
default per iPad). After sign-in the kiosk auto-pops a printable
AirPrint badge. Wall view filters per kiosk for multiple muster points.
Shipped.
Admin-editable, versioned NDA and safety briefing. Visitor must scroll to the bottom before acknowledging; NDA needs a drawn signature on the iPad (canvas → PNG, stored against the visit + version). A signed-NDA copy is emailed to the visitor.
Shipped.
Visitors are first-class: keyed by email, returning visitors get their name / company / phone pre-filled from a previous visit. If the same visitor acknowledged the current NDA version in the last 365 days, the kiosk skips the NDA step on this visit.
Shipped.
Hosts pre-book expected visitors; the visitor gets an email invite with a QR code. They scan the QR at the kiosk to claim the booking and skip the form fields they’re already on file for.
Shipped.
The iPad’s front camera captures a visitor photo at sign-in. It prints on the badge and is retained for 30 days against the visit record, then auto-purged by a daily sweep. Off by default — privacy is opt-in.
Shipped.
Optional ldapts lookup for hosts from the
visitas-world AD group. Same env-driven on/off pattern as cambiar;
if auth.ad.enabled = true and AD_BIND_PASSWORD is set
the host list pulls from AD, otherwise local-only.
Shipped.
Admin-uploadable logo (PNG / SVG / JPEG / WebP) and app name. Renders on the topbar, the login screen, the kiosk welcome card, and the printed badge. Light and dark themes, persisted per browser.
Every UI action is a documented HTTP endpoint. Build your own integrations, scripts, or in-house tools against the same surface the kiosk uses — no internal-only RPCs.
Three minor releases of operational + safety hardening on top of the 1.0 stable line. Each is a single feature, a single minor bump, a single CHANGELOG entry. No batch.
Token-keyed public endpoints (badge + photo URLs are unguessable 64-hex tokens, not sequential ids), PNG magic-byte validation on signature + photo upload, login rate limiting. From the v1.0 senior code review.
Shipped.
Admins and security users can ban visitors by record, by email, or by name + company substring. Banned attempts get a generic kiosk refusal, an audit row, and an email + SMS to all on-duty admin / security so reception intercepts at the door.
Shipped.
Notifications failure log so missing host emails are debuggable,
configurable photo retention (1–365 days, default 30),
admins-only mode for the /active wall view for sites
with NDA-sensitive client work, and a single
npm run preflight that runs everything CI runs.
Shipped.
Session cookie tightened to SameSite=Strict; AD
configuration is validated at startup so an enabled-but-
unauthenticated LDAP can’t silently fall back to an
anonymous bind. Confirms the PNG magic-byte check covers
signatures as well as photos. The rest of the v1.0 senior
review, closed.
Shipped.
Per-visitor right-to-be-forgotten endpoint
(DELETE /api/visitors/:id) that scrubs PII from
every visit, invitation, and notification log entry while
preserving the workshop’s audit shape. Plus a real
backup + restore section in the README (what’s in
data/, hot-backup with sqlite3 .backup,
nightly cron, restore procedure).
Shipped.
Honesty is part of the design. visitas.world is not:
git clone https://github.com/djsincla/visitas.git && cd visitas
cp .env.example .env
# set JWT_SECRET to something long and random
docker compose up -d --build
# open http://localhost:3000
# log in: admin / admin (forced password change on first login)
# kiosk surface lives at http://localhost:3000/kiosk
SQLite database persists in ./data/. Configuration in
./config/ (mounted read-only). Edit
config/visitor-form.json before first deploy to customize what
the kiosk asks visitors.
Bug reports, feature requests, and questions all go to GitHub Issues. There's no separate ticket queue, no support email, no “contact our team” form — every conversation lives in the open where the workshop and any contributors can see it.
better-sqlite3.ldapts, optional and disabled by default.nodemailer); SMS via a small Twilio adapter (v0.3).