Hosting and isolation
GetUp runs on Vercel for the web tier and Firebase Firestore + Cloud Storage for data. Each customer is a tenant — a top- level company doc with all child collections scoped under it. Cross-tenant access is blocked at the Firestore rules layer (every read explicitly checks request.auth.token.companyId == resource.companyId) and re-checked in every API route, so a bug in one layer doesn't expose another tenant's data.
Authentication
- Sessions — signed JWT in an HTTP-only, Secure, SameSite=Lax cookie. Cookie name
getup_session. Rotated on every sign-in. - Passwords — bcrypt-hashed at rest (work factor 12). We never store plaintext, and reset links use single-use 30-minute tokens that auto-invalidate when a new one is issued.
- TOTP — RFC 6238 second factor on the company portal. Pair any standard authenticator (Google Authenticator, 1Password, Authy). The shared secret is encrypted in Firestore; we never display it after the initial QR setup.
- Google SSO — optional on the company portal. The OAuth flow only requests the openid + profile + email scopes; we don't request Drive or Calendar access via the sign-in flow.
- Forgot-password — generic success response regardless of whether the email exists, so the endpoint can't be used to enumerate accounts.
Role-based access
Four customer-facing roles: company (owner / manager), staff (employee), accountant (external bookkeeper), tablet (anonymous kiosk). Every request carries a role; every API route checks it. The accountant role specifically cannot reach payroll or employee records under any path. Tablet sessions are scoped to a single location and can only mint check-in / check-out codes.
Encryption
- In transit — TLS 1.2+ everywhere, HSTS enabled. No plaintext fallback.
- At rest — Firestore + Cloud Storage are Google-encrypted at rest by default. On top of that we encrypt sensitive secrets (KSeF refresh tokens, integration OAuth refresh tokens, TOTP shared secrets) with AES-256-GCM using application-tier keys.
- KSeF session — invoice content is encrypted with an RSA-OAEP-wrapped AES-256-CBC session key before transit per the KSeF protocol; the encryption is symmetric to what the gateway expects, not a layer we added.
Logging and what we don't log
Two distinct logs:
- KSeF audit log — at
/api/ksef/audit. Records every request, response, status, and token rotation. Each company can view its own audit trail. - Activity log — sensitive actions per workspace: sign-ins, password changes, role changes, deletions. Auto-pruned after 12 months under GDPR Article 5(1)(c) (data minimisation).
Things we do not log:
- Passwords. Not in cleartext, not at any layer.
- Authorization headers. Tokens get redacted at the HTTP layer before logs are written.
- KSeF invoice contents. The audit log records that an invoice was submitted (idempotency key + KSeF reference) but not its line items.
- AI assistant prompts and responses. Per-tenant data flows through the assistant in-memory; we don't persist chat transcripts beyond the current session.
Retention
What stays, for how long:
- Operational data — kept while the workspace is active. Deleted with the workspace on day 30 of cancellation.
- Activity logs — auto-pruned after 12 months even on active workspaces.
- KSeF inbound + outbound XML — kept while the workspace is active for the company that owns them. KSeF documents already submitted to the Polish gateway remain on the gateway side per Polish law — only the local archive is removed when you delete the workspace.
- Backups — Firestore point-in-time recovery spans 7 days. After that, deletion is final.
- Stripe invoices — Stripe holds these per their own retention; they remain available even after we delete your workspace.
Export and delete
Two GDPR rights live in Account settings:
- Export my data — assembles a ZIP with every employee, shift, invoice, payroll period, KSeF document and audit-log entry, and emails it to the company owner. Full flow at Export your data, or delete the workspace.
- Delete workspace — schedules deletion with a 30-day grace window. Cancellable at any point during the window.
Reporting a vulnerability
If you find a security issue, please don't go public — email security@getup.dev with reproduction steps. Our RFC 9116 security.txt lists the contact, the canonical URL and the preferred language. We acknowledge within one business day and aim to ship a fix or mitigation before public disclosure.