IT provider manual
🖥️ For IT service providers
An empty reception desk in the evening
Manual 3 of 5 · IT provider manual · release r396

Rosie — architecture, security, operation

The technical reference for IT service providers and IT contacts at the customer. Swiss instance, security headers, app cache, web push, database migrations, backup, observability and escalation — all traceable. Provider names appear here deliberately, because you have to open firewall rules and document the outsourcing.

🖥️ IT 🇨🇭 Infomaniak (CH) 🔒 HSTS preload 📡 Web Vitals Release r396
🏗️

System architecture

A vanilla-JS web app on a Swiss instance

Rosie is a vanilla-JavaScript progressive web app. The application runs as a Node process on a Debian instance at Infomaniak in Switzerland, fronted by Caddy as a reverse proxy with automatic certificate management. Persistence uses libSQL/SQLite on the same instance (WAL mode), object storage via the S3-compatible object store at Infomaniak. The service worker uses a CACHE_PREFIX per release. A protective service sits in front of the public website — it holds no data; the API talks directly to the Swiss instance.

🖥️
Compute
Node on Debian at Infomaniak (CH), Caddy as the reverse proxy. No servers and no virtual machines at the customer.
🗄️
libSQL / SQLite
On the same instance, WAL mode, encrypted off-site backups. Data hosted in Switzerland.
📦
Object storage
Attachments and documents in Infomaniak’s S3-compatible object store (Switzerland).
🤖
AI is optional
Enabled per tenant. Which AI provider is used is stated in the sub-processor list of the data processing agreement — not in this manual, so that it does not become wrong when the provider changes.
ℹ️
The stack at a glance: Vanilla JS · Node on Debian (Infomaniak CH) · Caddy · libSQL/SQLite · S3-compatible object storage · service worker with a release prefix · web push. There is nothing to install at the customer.
🌍

Browser compatibility

Which browsers are supported
🟢
Chrome ≥ 90
Fully supported, including installing the app.
🦊
Firefox ≥ 88
Fully supported.
🔷
Edge ≥ 90
Fully supported, including installing the app.
🧭
Safari ≥ 14.1
Install via “Add to Home Screen”.
⚠️
Internet Explorer: not supported. iOS splash screens must be PNG: WebP has NOT been accepted since release r168 — iOS splash assets MUST be supplied as PNG.
🌐

DNS and domains

Main domain, alias and the app subdomain
  • Main domain: rosie-app.ch (including www).
  • Alias / mirror: rosie-planer.com — as a 301 redirect. Expect a cache window of about two minutes that resolves on its own. To check the origin immediately, use ?cb=<random>.
  • App subdomain: the application runs under its own name pointing directly at the Swiss instance (“DNS only”, with no intermediary service).
  • No wildcard record. *.rosie-app.ch does not exist — an invented name does not resolve. That is deliberate and rules out subdomain takeover.
  • The protective service in front covers the public website only; it holds no customer data.
💡
Cache window of the alias domain: After a deployment, the 301 redirect to rosie-planer.com may briefly come from the cache of the service in front. It resolves on its own. For forensics: an origin request with a cache buster.
🛡️

Security headers

HSTS preload, CSP, SRI, version forensics
🔒
HSTS (preload)
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload — set by the application itself, so the header applies regardless of the service in front. Submission to hstspreload.org is done manually.
🧱
Strict CSP
A strict content security policy, in production.
🧾
SRI
Subresource integrity on external scripts.
🔖
X-Worker-Version / X-Request-Id
In every response. WORKER_VERSION = RELEASE, imported from shared/version.mjs (a single source).
ℹ️
Verifiable in one command: curl -I https://rosie-app.ch shows every header that is set, including HSTS, CSP, X-Worker-Version and X-Request-Id.
⚠️
Pitfall: includeSubDomains applies to every subdomain, including test and staging names. And once delivered, an HSTS header stays valid in the browser for up to two years — it cannot be withdrawn at short notice.
📦

Service worker, asset cache

Cache prefix, upstream cache, lazy loading, iOS splash, resync
  • sw.js with CACHE_PREFIX, stamped from shared/version.mjs (scripts/sync-version.mjs).
  • Upstream cache: about two minutes of lag after a deployment, which resolves on its own.
  • immutablecache header for versioned assets.
  • Lazily loaded libraries via _loadVendor — XLSX, html2canvas.
  • iOS splash screens must be PNG — WebP dropped in release r168.
  • No background sync — deliberately: a replay without an open tab would have no fresh token and, without idempotency keys, would risk duplicate writes. The chat outbox only flushes while the app is open (see docs/OFFLINE.md).
  • Web push on iOS works only in the installed app — enforce adding it to the home screen.
💡
Deployment: RELEASE in shared/version.mjs then npm run version:sync — CACHE_PREFIX in sw.js / src/constants.js are stamped automatically; nothing needs updating by hand.
📡

Web push (RFC 8291)

VAPID, idempotency, quiet hours, categories
  • VAPID keys (public and private) are stored as secrets on the instance under /etc/rosie/env.
  • subscribeToPushNotifications is an idempotent upsert.
  • Quiet hours are respected.
  • On or off per alert category: bounty · planPublished · adminAlert · messages · absenceResult.
⚠️
Rotating the VAPID keys invalidates every existing push subscription (a known limitation) — plan the rotation carefully.
🔥

Firewall / network

Allow list, ports, polling
  • Outbound HTTPS on port 443 only.
  • Allow for rosie-app.ch and the app subdomain. No other destinations are needed — the app does not call any service at the customer on its own.
  • No inbound ports.
  • No WebSocket required — the app polls every 60 seconds.
📧

Email system

SMTP via a Swiss mailbox, SPF/DKIM/DMARC

Delivery via SMTP to a Swiss mailbox at Hostpoint. The sender of all system email is support@rosie-app.ch — an address you can reply to, not a no-reply dead end. SPF, DKIM and DMARC are configured; we recommend testing delivery against the customer’s spam filter once after initial setup.

💡
If invitation emails do not arrive: first check the quarantine and spam filter at the customer, then look up SPF/DKIM/DMARC for rosie-app.ch . Adding support@rosie-app.ch to the allow list resolves most cases.
📱

Mobile & MDM

iOS / Android, web clips, installation
  • iOS 14.5+, Android 8.0+.
  • A web app — no app store package.
  • MDM can configure the preferred browser, home screen web clips and trusted sites .
  • Note: Web push on iOS requires the installed app; iOS splash screen in PNG (not WebP).
🗃️

Database migrations and schema versions

Versioned, forward-only, verified after every run

Migrations live under database/migrations/ , numbered consecutively, and are applied forward only. The current state is in the directory itself — this page deliberately names no number, since it would be wrong by the next release. Every run is followed by a schema check. The migrations are built so that an empty database can be rebuilt entirely from them.

ℹ️
Migrations run as part of the deployment and need no intervention at the customer. There is no reverse run — the way back is through recovery (see Backup & Recovery).
💾

Backup & Recovery

Encrypted backups, recovery points, export
  • Automatic backups of the database, encrypted and stored off site.
  • A 30-day recovery window with hourly recovery points (RPO of one hour or less).
  • Object storage is backed up separately.
  • A full data export per tenant, at any time from the administration interface.
  • No geographic redundancy. One production instance is run deliberately — a restart or an update means a short outage. That is a known and accepted trade-off, and it can be added later without changing the architecture.
⚠️
Recovery times are not part of this manual. Only the service contract is binding.
↩️

Rollback / versions

Checkpoint tags, emergency procedure

Every release has a tag checkpoint-rNNN-deployed in the source control system, so it can always be shown which version was live and when.

ℹ️
Rollback: The previous release is kept on the instance and can be reactivated within minutes. A database migration does not roll back — if you need to go further back, use recovery.
Emergency procedure
1
Report to the vendor
Rollbacks on the instance are carried out by the vendor — with the release number and the time taken from the symptom.
2
Clear the browser cache
A hard reload, updating the service worker if needed.
3
Verify the headers
curl -I → X-Worker-Version to check.
🏢

Tenant isolation

Server-side scoping, reset on sign-out
  • Server-side tenant scoping — tenant_id in every query.
  • The in-memory tenant globals are cleared on sign-out via _resetTenantCalendarData() (six calendar globals).
  • Cross-tenant leak tests are pinned by regression cases.
  • Persistence is triggered by a tenant marker companySettingsDirty.
📊

Observability & Logs

Version, request id, Web Vitals, personal-data scrubbing
  • X-Worker-Version (r166 A10), X-Request-Id in every response.
  • Web Vitals → /api/analytics, sent with keepalive, Do Not Track respected.
  • A personal-data scrubber runs before analytics.
  • k-anonymity of at least 4 for aggregate charts.
  • Logs sit on the instance (systemd/journald) and are evaluated by the vendor; an external uptime monitor checks availability and raises alerts.
⚠️
Correction to the old manual: observability is NOT disabled — it is active, with personal-data scrubbing and respect for Do Not Track.
⚡

Performance budget

LCP / CLS / INP, lazy loading, battery saving
  • LCP / CLS / INP targets as the performance budget.
  • Layout-shift fix: width / height on <img>tags.
  • Lazily loaded libraries via _loadVendor — XLSX / html2canvas.
  • Battery saving: polling pauses when document.hidden (paused when hidden).
🔌

Key API endpoints

Routes with a version header
👥
Master data & Roster
/api/staff · /api/shift-assignments · /api/vacations
🤒
Absences
/api/absences · /api/absences/:id/{approve,reject,cancel} · /api/absence-approval-config
💬
Communication
/api/messages · /api/suggestions
📄
QMS
/api/qms/documents
📋
Tasks
/api/tasks · /api/tasks/templates · /api/tasks/:id/qms-links (PATCH) · /api/tasks/audit
🔄
Mutations / onboarding
/api/mutations · /api/onboarding/:step
📊
Analytics / log
/api/analytics · /api/log
👤
Profile
/api/profile
🔐
Month-end close
/r163-monatsabschluss · /r163-monatsabschluss/:year_month (POST / DELETE)
📰
Feed proxy
/api/feed-proxy
ℹ️
Every response carries a version header (X-Worker-Version) and a request id (X-Request-Id).
🤖

AI system & Data-protection firewall

Rate limit, personal-data sanitiser, speech recognition in the browser
  • AI provider: enabled per tenant; one fixed service per purpose, with no automatic fallback. Which provider is used is stated in the sub-processor list of the data processing agreement — not here, so that this page does not become wrong when the provider changes.
  • Rate limit: 30 requests per hour per user.
  • The personal-data sanitiser strips email addresses, social security numbers, IBANs, phone numbers and dates before every call to the AI service.
  • Scheduled AI jobs 0 2 / 0 4 for the task pattern learner — prompts without staff.id.
  • Speech recognition: handled by the browser (Web Speech API); depending on the browser or operating system, the recording is sent to its own speech recognition service — ROSIE neither receives nor stores any audio recordings. For command recognition, only the recognized text goes to the AI service, with consent (Art. 6(6) revFADP).
💡
Data-protection firewall: anonymised requests without identifiers; phone numbers, social security numbers, IBANs, email addresses and dates are removed before sending, names are not. For firewall rules at the customer: calls to the AI service originate from the instance, not from the workstation. Speech recognition, by contrast, is called by the browser from the workstation, at its provider’s service.
🧩

Module system

Optional modules per tenant

Optional modules: Quality management (QMS), Voice-to-Schedule, Payroll preparation & finance, Smart Matchmaker, Workload radar, Regional network. Enabled per tenant under Administration → Tools.

♿

Accessibility & Languages

Escape and focus trap, ARIA, four interface languages
  • A global Escape key and focus trap .
  • ARIA progress bar .
  • Four interface languages: DE / FR / IT / EN.
  • 14 chat languages for internal communication — the translation runs on the device.
🔑

Secrets management

Secrets on the instance · rotation · password hashing
  • Secrets live in /etc/rosie/env on the instance, readable only by the service account: the session signing key, the VAPID key pair, and credentials for object storage, SMTP and the AI service.
  • Rotate the signing key annually.
  • Rotating the VAPID keys invalidates push subscriptions (a known limitation).
  • Password hashing: PBKDF2-SHA-256 with a salt, in four chained rounds of 100,000 iterations each (400,000 in effect).
🚀

Initial setup for IT

What has to be done at the customer — and what does not

A tenant is set up by the vendor on the Swiss instance. No installation is needed at the customer — no servers, no agents, no inbound ports. What follows is the list of items that genuinely sit on the customer side.

After onboarding
1
Check reachability
curl -I against the app address — headers including X-Worker-Version to check.
2
Test email delivery
Send an invitation to a customer address and check the quarantine; support@rosie-app.ch to be allowed.
3
Sign the data processing agreement
Conclude the data processing agreement with Digital Passion GmbH — it is both the provider and the processor.
4
Create the administrator account
The tenant’s first administrator.
🛠️

Troubleshooting & Support

Procedures, escalation levels

Procedures exist for: sign-in errors · database errors · deployment errors · email delivery · AI outage · push delivery · migration errors.

ℹ️
Support: support@rosie-app.ch. Response times are governed by the service contract, not by this manual. For security reports, the disclosure channel is at /.well-known/security.txt.
Escalation levels
1️⃣
First level
Browser / cache / app.
2️⃣
Second level
Network, email delivery, MDM — everything on the customer side.
3️⃣
Third level
The vendor: server logs, database, release. Please report with the X-Request-Id and the time.
🔒

Verifying data protection

TLS / AES / hosting / agreement / single sign-on status
  • TLS 1.3.
  • AES-256 at rest. Particularly sensitive free-text fields are additionally encrypted field by field.
  • Data hosted in Switzerland (Infomaniak): compute, database, object storage and backups. The protective service in front of the public website and the payment provider are named in the sub-processor list of the data processing agreement.
  • Data processing agreement in place.
  • No cookies, no tracking — Do Not Track is respected.
  • Key destruction as the operational revocation; erasure within 30 days of the end of the contract, with written evidence.
  • SSO (SAML/OIDC) is not available and under evaluation; no committed date exists. Sign-in uses email and password, optionally with TOTP.
❓

FAQ for IT

The most common IT questions, answered briefly
Where is the data stored?▶
In Switzerland at Infomaniak: the database (libSQL/SQLite) and an S3-compatible object store. Encrypted in transit (TLS 1.3) and at rest (AES-256). The contractual position is governed by the data processing agreement; its sub-processor list names everyone involved.
Which firewall rules are needed?▶
Outbound HTTPS on port 443 only. No inbound ports. Allowing rosie-app.ch and the app subdomain is enough — calls to the AI service originate from the instance, not from the workstation.
Is single sign-on available?▶
No. SSO (SAML/OIDC) is not available and is under evaluation; no committed date exists. Sign-in uses email and password, optionally with TOTP as a second factor.
Is observability disabled?▶
Correction: observability is ACTIVE — with personal-data scrubbing and respect for Do Not Track. Only anonymised aggregates are collected.
How are passwords hashed?▶
PBKDF2-SHA-256 with a salt, in four chained rounds of 100,000 iterations — 400,000 in effect.
Why is WebP not allowed for the iOS splash screen?▶
iOS accepts only PNG for splash screens. WebP was dropped for these assets in release r168 — the iOS splash screen MUST be PNG.
How does push work on iOS?▶
Web push on iOS works only in the installed app. MDM recommendation: enforce a web clip on the home screen.
Why is rosie-planer.com briefly out of date after a deployment?▶
The alias domain’s 301 redirect sits behind the protective service and may briefly come from its cache. It resolves on its own. For forensics: an origin request with a cache buster (?cb=<random>).
How do I identify the running version during an incident?▶
curl -I against the domain — the X-Worker-Version header gives the active release number, and X-Request-Id identifies the individual request for correlation in the server logs.

Rosie IT provider manual · version 9.9 · r396
Provider and processor: Digital Passion GmbH, Haldenstrasse 16, 4600 Olten, Switzerland · CHE-154.512.796
Questions? support@rosie-app.ch · rosie-app.ch
← Back to the overview