Security architecture
Hosting and infrastructure
Twine runs on virtual servers hosted in Sweden. All customer data is stored and processed within the EU.- Network isolation. Application servers reach the database over a private software-defined network. The database is a managed PostgreSQL instance that is not exposed to the public internet.
- Origin lock-down. All public traffic passes through Cloudflare, and the load balancer rejects any connection that does not arrive from Cloudflare’s published address ranges. The origin cannot be reached directly, so Cloudflare’s TLS termination, filtering, and edge protection cannot be bypassed. The allowlist is synchronised from Cloudflare’s published ranges automatically rather than maintained by hand, so it cannot silently go stale as those ranges change.
- Immutable servers. Every release provisions fresh servers from a template rather than mutating running ones. Deployments are blue/green: new servers are added to the load balancer only after passing health checks, and the previous generation is drained and destroyed.
- Ordered migrations. Database migrations run before a server is allowed to accept traffic, so no request is ever served by a partially migrated node.
- Least privilege on the host. The application runs as an unprivileged system user. SSH is not reachable from the internet at all, and where it is reachable it accepts key authentication only, with no password login. Runtime secrets are provisioned out of band, are never present in the application image or in source control, and are not readable by the application user’s shell environment.
Authentication
Users sign in with Google or Microsoft OAuth. Twine does not issue, store, or verify passwords for its own application. This means password policy, multi-factor authentication, conditional access, and account deprovisioning are all governed by your own identity provider, and revoking a user there immediately removes their ability to sign in to Twine. Sessions use signed cookies withSameSite=Lax and a 24 hour lifetime, backed by server-side session tokens that can be revoked. Session identifiers are rotated on login to prevent session fixation, and signing out invalidates the server-side token and disconnects any open live sessions.
Machine access (the Public API, SCIM, and the MCP server) uses OAuth 2.0 bearer tokens issued per role rather than per user. Tokens are recorded in the database, which makes them individually revocable, and carry a short access-token lifetime with a separate refresh flow. Refresh requires proof of possession, and every failure mode returns an identical response so tokens cannot be probed for validity.
Authorization
Access control is expressed in a single model used consistently across the web application, the API, and the MCP server. A user holds one or more roles, each role carries rules, and each rule combines:- a policy, either allow or deny
- a set of actions:
get,list,create,update,delete,administrate - one or more resource locators, which name a resource type (employee, time report, system integration, job, role, and so on), optionally a specific resource, the owning organization, and optionally an explicit list of permitted fields
Tenant isolation
Twine is multi-tenant, and tenant separation is enforced in the data access layer rather than left to individual queries. Every database query is automatically constrained to the current organization, and a query issued without an organization context raises an error instead of returning data. Isolation therefore fails closed: a missing scope is an outage, never a leak.Application security
- Content Security Policy with a per-request cryptographic nonce. Inline scripts and event handlers are not permitted anywhere in the application.
- Clickjacking protection. Twine cannot be embedded in a frame by any origin, which prevents overlay attacks against an authenticated session.
- CSRF protection on all authenticated browser requests, plus standard secure browser headers and no-cache directives on authenticated pages.
- CORS restricted to Twine’s own application origin.
- Endpoint separation. The customer application, the Public API, the unauthenticated edge (OAuth callbacks and inbound webhooks), and the MCP server run as four separate endpoints with separate configuration and separate signing keys. The unauthenticated edge has no session layer at all, structurally preventing session-based access decisions on untrusted traffic.
- Rate limiting. The Public API is limited per authenticated role, SCIM per system integration, and unauthenticated token endpoints per token identity. Limits are communicated back in standard response headers.
- Runtime protection. A web application firewall layer inspects and can block requests at the edge of the application.
Secure development and supply chain
- Every build compiles with warnings treated as errors and runs the full automated test suite.
- Static security analysis runs on every branch and pull request, with findings surfaced directly on the pull request.
- Every build is scanned for malicious Unicode (bidirectional overrides and homoglyph attacks) to defend against trojan-source style contributions.
- Third-party CI actions are pinned by commit hash, and externally downloaded build tooling is verified against a known checksum.
- Generated source files are verified against a regeneration check at build time, so what is deployed is exactly what was reviewed.
Audit logging and monitoring
Twine keeps three distinct audit trails:- Change data capture at the database level for security-relevant tables: users, roles, rules, permission grants, API tokens, system integrations, and domain mappings. Every change records who made it and in which organization. Credential columns are explicitly excluded from capture, so secrets are never copied into audit records.
- A customer data access log recording which user viewed which records, retained for 365 days. The log is structurally prevented from containing free-form text or personal data; entries carry identifiers only.
- API access logging for every authenticated Public API and SCIM request.
Technical integration setup
How Twine connects to your systems
Twine is a hosted service. There is no agent to install, no VPN, and no inbound network access to your infrastructure. Connectivity is established in one of three ways:- Outbound API calls. Twine authenticates to a system’s API using OAuth or an API key that you provide, and calls it over HTTPS. This is the most common setup.
- Inbound webhooks. A system pushes change notifications to a dedicated Twine endpoint. Each integration has its own URL and its own secret, and requests are verified by HMAC signature or basic authentication before anything is processed.
- File transfer. Twine can read from and write to SFTP servers and S3-compatible object storage, and can host an SFTP endpoint that your systems upload to, authenticated with per-integration SSH public keys.
Scheduling and triggering
Each domain mapping carries its own sync trigger with a cron expression, so different data types can run at different cadences. Twine evaluates triggers every minute. A trigger whose previous run has not finished is not started again, so runs of the same integration never overlap. Webhook-driven integrations are coalesced: a burst of notifications arriving within a configured window collapses into a single run, which prevents a bulk change in a source system from producing hundreds of redundant syncs.Process isolation: how one bad record cannot break a sync
This is the central design decision in Twine’s pipeline. A sync does not process employees in a single long-running batch. Instead:- A sync job reads the full set of records from the source system once, maps them into Twine’s data model, and stores them.
- It then creates one independent replication job per record, per destination system. For 500 employees replicated to two payroll systems, that is 1000 separate jobs.
- Each job is picked up separately and executes in its own process, its own database transaction, and its own HTTP session.
- Failures are contained. If employee A has a malformed personal identity number, a missing required field, or triggers a validation error in the destination system, only employee A’s job fails. The other 499 complete normally and their data is committed. Partial success is a normal, expected outcome rather than an error state.
- Failures are attributable. You do not see “sync failed”. You see a job tree: 500 records, 496 completed, 3 completed with issues, 1 failed, with a per-record log explaining exactly which field on which record caused the problem and which transformation step produced it.
- Retries are safe. A failing record can be corrected at the source and re-run on its own, without reprocessing or resending the records that already succeeded.
Failure classification and back pressure
Twine classifies every error rather than treating all failures alike:
On top of this sit two protective mechanisms:
- Rate limiting per vendor. Twine maintains a rate limiter per integrated system, respecting each vendor’s documented limits and, where available, their own rate limit response headers. Requests queue locally rather than being rejected remotely.
- Circuit breakers. A breaker can halt pipeline work at the level of a single domain mapping, a system integration, an entire system, or an entire organization. Breakers trip manually, on sustained error rates, on a vendor’s published status page reporting an incident, or on an unexpectedly large change magnitude. A tripped breaker stops work rather than repeatedly hammering a failing system, and the reason is visible on every affected job.
Idempotency and change detection
Re-running a sync is safe by design:- Every entity carries a content hash. If a sync produces an identical result, the record is left untouched and no downstream replication is triggered. Unchanged data does not manufacture change events.
- Writes to destination systems go through a diff engine that reconciles the source timeline against the destination’s current timeline and emits only the necessary create, update, and delete operations, constrained by what the destination system is actually capable of accepting.
Data transformation
Data is not passed through verbatim. Each source property is mapped to a property in Twine’s internal model, and from there to whatever each destination expects. Mappings can be direct, can pass through converters, or can be expressed as a visual node graph in the Data Engine. Twine’s model is date-tracked: values are stored with the date they take effect, so a salary change or a department move is a new entry on a timeline rather than an overwrite. Because mappings are explicit, Twine only fetches and stores the fields you have mapped. Unmapped fields in a source system are not persisted.Programmatic access
The Public API exposes the mapped data over REST with an OpenAPI specification, authenticated and authorized under the same role and field-level model described above. SCIM 2.0 is supported for identity provisioning. Every request is access logged.How personal data is protected, stored, and transferred
What data Twine processes
Twine processes employee-related data as required by the integrations you configure. Typical categories include name and contact details, employment identifiers, employment terms and history, organizational placement, salary and compensation data, schedules, absence and time reporting, and, where a destination system requires it, national identity numbers. The exact set is determined by your property mappings. Twine acts as a data processor on your behalf.Data location
All customer data is stored and processed in Sweden. Object storage is located in the EU. Application logs and monitoring telemetry contain no personal data.Protection in transit
- Every connection into Twine, whether from a browser, the API, or an inbound webhook, requires HTTPS with a minimum of TLS 1.2. TLS 1.0 and 1.1 are refused.
- Plain HTTP requests are redirected to HTTPS, and every application host sends HTTP Strict Transport Security, instructing browsers never to contact Twine over an unencrypted connection and to refuse certificate warnings rather than allowing a user to click through them.
- Outbound calls to integrated systems are always encrypted, but the negotiated TLS version is bounded by what each vendor’s API supports. Twine cannot raise a third-party system above the protocol version that system offers.
- Connections between the application and the database are TLS encrypted with full certificate verification against a pinned certificate authority, not merely encrypted.
- File transfer uses SFTP over SSH with verified host keys, or HTTPS for object storage.
- Inbound webhooks are verified by signature before their payload is processed.
Protection at rest
Personal data is protected by two independent layers of encryption. The hosting provider encrypts the database storage itself with AES-256, and Twine additionally encrypts personal data in the application before it is written, so the database never holds readable values even from the inside. Obtaining the underlying disk therefore yields nothing without defeating both layers, which use separately held keys. The application layer works as follows:- The cipher is AES-256-GCM, which is authenticated encryption: tampering with stored data is detected, not silently decrypted.
- The master key is held in a managed key store, separate from the database and separate from the application servers, and is loaded into memory at boot. Someone with a copy of the database, a backup, or a disk image cannot read personal data from it.
- The entire employee property set is encrypted, as are organizational units, time reports, schedules, projects, customers, competences, salary transactions, notes, job logs, flow run logs, and anomaly details.
Backups
The database is backed up continuously, with point-in-time recovery covering the most recent 24 hours and scheduled backups retained for 31 days. Recovery can therefore target either an arbitrary moment within the last day or a specific daily restore point within the last month. Because personal data is encrypted by the application before it reaches the database, backups contain ciphertext only. A backup obtained without the corresponding key, which is stored separately from both the database and the backups, yields no readable personal data.Access to personal data
- Every query is automatically scoped to a single organization and fails closed if that scope is absent.
- Authorization is enforced down to individual fields, so an integration or API consumer can be granted the minimum property set it needs.
- Views of customer data by Twine users are recorded in the access log, retained for 365 days.
- Application logs at normal severity levels, and the data sent to monitoring services, deliberately exclude personal data.
Retention and deletion
Retention is bounded by default and configurable where it makes sense:
Retention sweeps run automatically on a schedule. Deletion in Twine is permanent, not a soft delete, and deleted records are removed rather than flagged. Deleted data may persist in encrypted database backups until those backups age out, at most 31 days, after which it is unrecoverable. Requests to delete an individual’s data, or to export it, can be made through your Twine contact or [email protected].
Infrastructure providers
Operating Twine involves the following providers:No customer data is stored with our key management provider; it holds key material only. Contact us for the current sub-processor list with the applicable legal terms.