Skip to main content

Data, privacy & backups

Every Operentra workspace owns a slice of one shared database, plus the files its users upload. This page explains what happens to that data: how a workspace can export or snapshot everything it holds, how sensitive values are encrypted at rest, and how data is permanently removed — both a single employee (right to erasure) and an entire workspace (operator termination).

Two roles do this work in two places:

  • A workspace admin manages their own exports, backups and employee erasure from Data & Privacy at /admin/company/data-privacy (requires the company.settings permission).
  • The platform operator permanently erases a whole workspace from the operator console at /operator.

Exporting workspace data

The Export all company data button downloads a single machine-readable JSON file containing every record in the workspace — employees, attendance, payroll, documents and configuration — reachable from the Company row through the data model. It answers GDPR Article 15/20 (access & portability) requests.

Exports are redacted: fields that are either useless outside the system or a security liability in a portable file are replaced with [REDACTED]. These include:

  • account credentials (passwords, refresh tokens, password-reset tokens);
  • biometric fingerprint templates;
  • live capability tokens (careers offer / interview-confirmation links);
  • encrypted integration and storage credentials.

There is also a per-employee export for a single subject access request, available once you pick an employee on the same page. It downloads only that employee's records, scoped so it can never leak another employee's data.

note

An export is a redacted, read-only copy for a person. It is not a backup — it cannot be restored. Use the backups below to preserve or roll back a workspace.

Encryption at rest

Sensitive data is encrypted with AES-256-GCM using a single key, APP_ENCRYPTION_KEY, held in the server environment (never in the database). The key is a base64-encoded 32-byte value; each encrypted blob stores its own random IV and authentication tag, so tampering is detected on decrypt.

The same key protects:

  • per-workspace backups (the snapshot files described below);
  • biometric fingerprint templates;
  • stored secrets — storage-destination credentials, integration API keys and the platform payment-gateway keys — which are held encrypted and only decrypted in memory when used.
warning

APP_ENCRYPTION_KEY is the master secret for everything above. If it is lost, encrypted backups and biometric templates cannot be recovered; if it leaks, that protection is gone. Keep it backed up separately from the database and rotate it deliberately.

Per-workspace backups

A backup is a full, unredacted snapshot of the entire workspace dataset, serialized to JSON and encrypted at rest with APP_ENCRYPTION_KEY. Unlike an export it keeps password hashes and everything else needed to rebuild the workspace, so the complete artifact never leaves the server (downloads are redacted — see below).

Snapshot artifacts are stored durability-first, under a per-workspace key:

backups/tenants/<companyId>/<timestamp>-<id>.json.enc
  • When an S3-compatible storage destination is configured, the artifact is written there (the active destination when it is S3-backed, otherwise the first configured bucket). This is required on cloud hosts where the container filesystem is ephemeral and per-instance — a snapshot on container disk would silently vanish on the next deploy.
  • With no S3 destination on a durable host (a conventional server install), the same key is used as a path under <APP_ROOT>/ on local disk.
  • With no S3 destination on an ephemeral host (EPHEMERAL_LOCAL_STORAGE), backups refuse to run rather than pretend: connect an S3-compatible destination first.

Reads probe every S3 destination and then the disk location, so snapshots taken before a storage switch stay retrievable. The key deliberately sits outside the tenants/<companyId>/ upload prefix and is never served by the public /api/uploads/ route.

Each run is tracked by a TenantBackup row recording its status (running / completed / failed), what triggered it (manual / cron), size, row and model counts, and a SHA-256 checksum of the snapshot. A failed run keeps its error visible and is never silently cleaned up.

When backups run

TriggerHow it fires
NightlyA scheduled job at 02:00 (server time) backs up every active workspace; a per-workspace failure is isolated and never stops the others.
On demandThe Back up now button on the Data & Privacy page.
Pre-restoreTaken automatically just before a restore, as a safety net (see below).

Only the newest 14 completed backups are kept per workspace; older completed snapshots are pruned along with their files.

Downloading a backup

A completed backup can be downloaded from the backups list. The file is decrypted on the way out, so the download is plain JSON — treat it as sensitive, since it holds the workspace's full HR dataset.

Downloads are credential-redacted: the same fields the GDPR export strips (password hashes, refresh and reset tokens, TOTP secrets and recovery codes, encrypted integration credentials, biometric templates, live capability tokens) are replaced with [REDACTED], and the file's meta.redacted flag is set. The server-side artifact stays complete — restore always reads the stored copy, never a download, so redaction costs nothing at restore time.

Restoring a workspace

Restoring rolls the whole workspace back to a chosen snapshot. It:

  1. takes a fresh pre-restore safety backup of the current state first;
  2. wipes the workspace's current rows and re-inserts the snapshot's rows in dependency order, inside one transaction;
  3. requires you to type the exact workspace name to confirm.

A few things are deliberately kept as-is and never overwritten by a restore:

  • the Company row itself (updated in place — everything points to it);
  • the subscription / billing state (Stripe is the source of truth);
  • the audit log (history must survive a restore).
warning

Uploaded files (avatars, documents) are not versioned with snapshots. A restored record may reference a file that was deleted after the snapshot was taken. Everything since the chosen backup is replaced — restore only when you mean to discard newer changes.

Erasing a single employee

The right-to-erasure control on the Data & Privacy page permanently deletes one employee and every dependent record — attendance, payroll history, documents and the login account — in one transaction. To guard against mistakes it requires typing the employee's exact employee code to confirm, and it writes an audit entry naming who erased whom.

warning

Employee erasure is irreversible and is not blocked by statutory retention rules — confirm any tax or payroll retention obligations are met before you erase.

Erasing an entire workspace

Only the platform operator can permanently erase a whole workspace, from the companies list in the operator console. This is the terminal step when a tenant leaves, and it is heavily guarded:

  • the workspace must already be suspended (a separate, earlier action);
  • the operator cannot erase their own company;
  • the confirmation text must match the workspace name exactly.

When those checks pass, erasure:

  1. cancels the live Stripe subscription first, so a deleted tenant is not still charged — a cancel failure aborts the erasure before anything is deleted;
  2. writes one final encrypted snapshot (final-erasure-*.json.enc under the workspace's backup key prefix, stored durability-first like regular backups) that outlives the database rows, so a genuine retention obligation can still be met — the nightly retention sweep destroys it automatically once the cold-storage window ends;
  3. deletes every row the workspace owns, its backup records, and the company itself, in one transaction;
  4. flushes the workspace's uploaded files from every storage destination;
  5. writes an audit row (with the company reference nulled, since the company is gone) that records the row count, the final snapshot's path and its SHA-256.

How uploads are stored

Uploaded files (logos, seals, employee documents, receipts, careers CVs) do not live in the database. They are handled by a storage layer with two backends:

  • Local disk — the built-in default, rooted at the server's upload directory;
  • an S3-compatible bucket — added and activated by the operator under Settings → Storage in the operator console.

Uploads land on exactly one active destination, but reads fall back across the others, so switching the active backend never breaks links to files that have not migrated yet. Files are served under /api/uploads/ and are read back by a stable, UUID-prefixed key.

Every new upload is written under a per-workspace prefix so a tenant's entire file footprint sits in one place:

tenants/<companyId>/<category>/<file>

That prefix is what lets workspace erasure flush a terminated tenant's files cleanly, and what keeps one workspace's uploads separate from another's.

note

Upload files are stored as-is on disk or in the bucket — they are not individually encrypted by the application. Encryption at rest for uploads, when required, is a property of the underlying storage (disk or bucket) you host them on. The values encrypted with APP_ENCRYPTION_KEY are backups, biometric templates and stored secrets — not general uploads.

  • Tenant isolation and the operator boundary: see the multi-tenancy concept page.