Tidenda

Concepts

Security

Tenant isolation, authentication, signing, key custody, customer-side responsibilities.

Security

This page describes Tidenda's security posture at a level useful to an administrator, a procurement reviewer, or an integrator deciding how much to trust the platform. It covers what the platform protects, how, and where the integrator's responsibility starts. Cryptographic details and the verification recipe live in Signing and signatures.

Tenant isolation

Every document, every API key, every webhook, every workflow, every AI operation, and every audit record is scoped to a tenant. The platform enforces this scope at every read and write — there is no cross-tenant path through any public surface or any operator action.

  • API keys identify a single tenant. The Authorization header determines tenant identity; payload fields claiming a different tenant are ignored.
  • Operator sessions carry a tenant claim from the identity provider. The active tenant is visible in the application and an operator's documents are filtered to that tenant alone.
  • Storage keys (object store, database rows, search indices) carry the tenant id and are queried with the tenant filter applied at every layer.
  • Outbound webhooks fire only for events on the tenant that owns the webhook subscription. A webhook on tenant A never receives an event from tenant B.

A tenant administrator can delete the tenant's data through the admin interface; the action is destructive and requires multiple confirmation steps because there is no in-platform "undelete."

Authentication

Operators

Operators authenticate through an OpenID Connect identity provider (Keycloak in the reference deployment). The platform doesn't store operator passwords; the identity provider owns the credential lifecycle, password rotation, MFA, federation, and any organisation directory.

Sessions are HTTP-only, secure cookies. Refresh tokens are held server-side; the browser only sees a session cookie. Sign-out clears the local session and ends the upstream SSO session as well, so the next sign-in always shows the identity provider's login form.

Integrators

Integrators authenticate with per-tenant API keys issued by an organisation administrator. Keys are random 32-byte secrets, stored as salted hashes. The plaintext is shown to the administrator exactly once at creation; the platform cannot recover it. Lost keys must be reissued.

Each key carries a set of scopes. A key with documents:create cannot read phrases; a key with phrases:read cannot create documents. New endpoints declare their own scopes — existing keys keep working with their existing scope set until an administrator grants the new scope.

Authorisation

Two authorisation models, layered:

  • Tenant scope governs what an authenticated identity can see. Scope is checked before any authorisation rule runs. A request for a document outside the active tenant's scope returns 404 Not Found rather than 403 Forbidden, so the existence of the resource isn't leaked.
  • Roles govern what that identity can do inside the one tenant the request is scoped to.

Roles are per-tenant, not per-user

A role is a property of a membership — the pairing of one user with one organisation — and never a property of the user account itself. The same person can be an owner of one organisation, an editor in a second, and unknown to a third. Nothing they hold in one organisation carries into another.

This matters for a reader evaluating shared-tenancy risk: there is no account-level or directory-level attribute that confers privilege across organisations. Adding a user to a second organisation cannot widen what they can do in the first, because the two memberships are separate records with separate role sets.

The identity provider answers exactly one question — who is this person — and no question about what they may do.

The roles

Role Intended for Grants
Owner Whoever is accountable for the organisation Everything an admin can do, plus granting ownership and permanently deleting the organisation
Admin Day-to-day administration Members and their roles, API keys, webhooks, workflows, AI configuration, taxonomy and spelling, media deletion, organisation settings — plus everything a publisher can do
Publisher Whoever controls what reaches the public Publication and publisher configuration, plus everything an editor can do
Editor Editorial staff Create and edit content

Each role contains the one below it, so a single role is usually enough. A member may hold several; their capabilities are the union. New members receive Editor unless the inviter chooses otherwise.

Only two capabilities are reserved to Owner alone: granting the owner role, and permanently deleting the organisation and everything in it. Every other administrative capability is available to an admin, so an organisation can delegate its administration without delegating its existence.

Guarantees

Four properties hold regardless of how roles are administered:

Privilege cannot be self-granted. A member may only grant roles they already hold themselves. An admin can therefore create another admin, but cannot create an owner — no path exists from any role to a strictly higher one without an existing holder of that higher role acting deliberately.

An organisation always has an owner. The last remaining owner cannot be demoted, suspended, or removed. The request is refused rather than partially applied. Ownership is transferred by granting it to someone else first.

Revocation takes effect on the next request. Roles are read at the moment a request is authorised, not carried inside the access token. Removing a role does not wait for a session to expire or a token to be refreshed, and there is no window in which a revoked member still holds their old privileges.

Every role change is recorded. Grants, revocations, suspensions, removals, and the addition of the founding owner are written to an append-only audit record as part of the same transaction that applies them. Each entry names the actor, the subject, the roles before and after, and the time. A change that is refused — a self-escalation attempt, or removing the last owner — is recorded as refused, with its reason. Administrators can read their own organisation's record.

Enforcement

Endpoints require a permission, not a role. Roles exist to be assigned to people; permissions are what the platform actually checks. A member holds a permission if any of their roles in the active tenant confers it.

Suspending a member leaves the membership record intact but confers no permissions, so suspension and removal are equivalent in what they allow — the difference is only whether the history and the prior roles are retained.

Transport security

All public endpoints require HTTPS. The reference deployment terminates TLS at the edge with automatic certificate provisioning and renewal. Plain HTTP is refused outright for non-localhost origins.

The application enforces:

  • Strict-Transport-Security with a one-year max-age and includeSubDomains.
  • HTTPS-only cookies.
  • TLS 1.2+ at the terminator. TLS 1.3 where the load balancer supports it.

Webhook delivery security

Outbound webhooks carry two layers of integrity:

  • HMAC-SHA256 over the request body and the dispatch timestamp. Proves the request came from someone who holds the webhook's shared secret (the platform — or anyone with whom the secret has been shared). Use this to gate request acceptance.
  • Ed25519 over the document snapshot bytes. Proves the snapshot content itself was produced by Tidenda and has not been modified in transit. Use this when forwarding content to a third party.

A receiver should verify the HMAC before reading the body. The platform never logs secrets and never exposes them through the API after creation; if a receiver believes a secret may have been compromised, the administrator's response is to delete the webhook and create a new one. There is no in-place secret rotation today.

See Receiving webhooks and Signing and signatures.

Signing-key custody

Snapshot-signing keys are Ed25519 and rotate on the administrator's schedule. The active private key lives in deployment secrets — never in source control, never in plaintext at rest in the database, never on operator hardware. Historical public keys stay in the public trust list so previously signed content keeps verifying.

The reference deployment uses environment-variable-mounted secrets; larger deployments can plug in a dedicated secret manager.

Data at rest

  • Documents live in an object store. The reference deployment uses S3 with server-side encryption (AES-256) and bucket-policy isolation per tenant.
  • Indices and relational data live in PostgreSQL with the storage volume encrypted at rest.
  • Caches and event streams live in Redis. Caches are reconstructible from the source of truth; the event streams are short-lived and reconstructible from the persistent store.
  • Secrets (API key hashes, webhook secrets, signing private keys) live in the same storage as the rest of the data but are either hashed (API keys, webhook secrets) or held in deployment secrets (signing private keys).

Audit log

The platform maintains an append-only audit log for actions an administrator might need to investigate: workflow transitions, API key creation and deletion, webhook creation, sign-in events, and tenant-level destructive actions. Each entry records the actor (an identified operator, an API key, or a named system component), the target, and a timestamp.

Workflow transitions are also materialised into a queryable shadow table so administrators can answer "who transitioned this document when" without loading every document individually. The document's own history record remains the source of truth; the shadow table trails it and is rebuildable from event replay.

Audit entries are tenant-scoped — a tenant administrator can review their own tenant's history but not anyone else's.

Customer-side responsibilities

Some things sit on the integrator's side and the platform cannot enforce them:

  • API key storage. A leaked API key authorises the actions its scopes allow. Treat keys as secrets equivalent to a password: store them in a secret manager, rotate on suspicion of compromise, never commit them to source control.
  • Webhook endpoint hardening. The platform delivers signed payloads; the receiver decides whether to accept them. Verifying the signature, terminating TLS correctly, and not logging payload contents from rejected requests are the receiver's responsibility.
  • Downstream verification. If snapshot content is republished, the downstream consumers should verify the Ed25519 signature. Tidenda cannot enforce verification at the consumer end; making it easy is what we can offer (see Signing and signatures and the verifier samples).

Reporting a security issue

Please report suspected vulnerabilities privately. Contact the operator of the deployment you're using; for the Tidenda project itself, see the contact information on the project's home page.

Please do not disclose vulnerabilities through public issues or chat channels until the operator has had a reasonable opportunity to respond.

Related pages