Skip to content
Atelyea product of Murat Gözel

Security Overview

Version
1
Effective date

This page describes how the Atelye platform is secured. It is written for customers evaluating the platform, and it doubles as Annex II — the technical and organisational measures — of the Data Processing Addendum.

Every control described here exists today. Nothing on this page is a plan, a roadmap item or an intention. Where something is not yet built, this page says so rather than describing it in the present tense — see "What Atelye does not have" near the end, which is the section worth reading first if you are assessing risk.

Atelye is operated by Murat Gözel, a sole proprietor established in Türkiye. Its infrastructure is in the European Union.

Where the data lives

  • Compute and databases: Hetzner Online GmbH, Frankfurt, Germany.
  • Object storage — media, generated documents, exports, archived telemetry: Cloudflare R2, European Union.
  • Search indexing, authorisation, job execution and observability run on Atelye's own Hetzner infrastructure. They are not third-party services and data handled by them does not leave that infrastructure.

The full list of third parties that process personal data, with each one's role and location, is at /legal/subprocessors.

Separation between customers

The platform is multi-tenant: several customers' data lives in one database. Separation is enforced twice, deliberately, so that a mistake in one layer does not become a data leak.

  • PostgreSQL row-level security. Every customer-owned table carries row-level security and an isolation policy, written as explicit SQL in the same database migration that creates the table. The database itself refuses to return another customer's rows, independently of the application.
  • An application-level filter on top of it. Every request opens a transaction and sets the customer context for the duration of that transaction before any query runs. Queries filter on the same customer identity.

This is defence in depth rather than belt-and-braces decoration: the application filter catches a logic error, and row-level security catches a missing filter.

Cross-customer isolation is covered by an automated test suite that attempts to read, write, update and delete another customer's rows through each access path and asserts that every attempt fails. Those tests are in a protected directory: weakening one is a change that has to be argued for, not a change that can pass unnoticed.

Authorisation

Every action a user attempts is checked against a policy decision point (Cerbos) before it is carried out. Policies are versioned in the same repository as the application, so a change to who may do what is reviewed like any other change.

The server-side check at the moment of execution is the boundary. The interface hides actions a user may not perform, but that is a convenience, not the control — a request for a forbidden action is refused by the server regardless of what the interface displayed.

The authorisation policy test suite runs in continuous integration and must pass before a release can be deployed.

Authentication

Sign-in is passwordless. A user enters an email address or a phone number and receives a one-time code. There is no password to reuse, to phish at scale, to leak in a breach elsewhere, or to store.

Sessions are managed by Better Auth. Account, session and credential records live in the same database, under the same operational controls as the rest.

Secrets

Production secrets — database credentials, API keys, signing keys — are encrypted with SOPS using an age key, and are committed to the deployment repository only in encrypted form. They are decrypted on the production host at deploy time. They are never stored in plaintext in a repository, never included in a build artifact, and never sent to a browser.

Network and transport

  • TLS everywhere. TLS 1.2 and 1.3 are terminated at the host web server. Plain HTTP redirects to HTTPS. HSTS is set with a long max-age and includeSubDomains.
  • Nothing but the web server is reachable from the internet. The application listens on the loopback interface only and is reached through the web server's reverse proxy. The database, the connection pooler, the job engine, the search index and the observability stack publish no internet-facing port at all. The observability interface is reachable only through an authenticated tunnel by the operator.
  • Response headers on served pages include X-Content-Type-Options: nosniff, X-Frame-Options, Referrer-Policy: strict-origin-when-cross-origin, and Strict-Transport-Security. The applications set their own Content Security Policy, which admits no third-party origin.

Encryption

  • In transit: TLS, as above, for traffic from browsers and from messaging platforms, and for the operator's administrative access.
  • Database backups: encrypted client-side, on the host, before they leave it, with AES-256. The encryption key is held in the SOPS-managed secret set, not by the storage provider, so the provider holds ciphertext it cannot read.
  • At rest, on the live systems: disk-level encryption is a property of the infrastructure and storage providers, under their own terms. Atelye does not operate an additional at-rest encryption layer over the live database, and does not claim one. If at-rest encryption of the live database under a key you or Atelye controls is a requirement for you, say so before you commit — it is a gap, not a feature.

Record of activity

Every state-changing action writes a record — who acted, what action, on which record, what changed state, and when — in the same database transaction as the change itself. A change that succeeds without its record, or a record without its change, is not possible.

The record is append-only, enforced by the database and not by convention: the role the application connects as has had UPDATE and DELETE permissions revoked on the activity table and on every one of its partitions. The application cannot rewrite history even if its own code were made to try. Records are partitioned by month.

The activity record carries references and state transitions, not content. It records that an order line changed and points at the record; it does not embed a buyer's address in a before-and- after comparison. It records that a conversation moved state; it does not embed the message. This is what makes a deletion request meaningful — a record that kept a copy of the content would have deleted nothing.

Monitoring

The platform emits OpenTelemetry logs, traces and metrics to OpenObserve, self-hosted on Atelye's own infrastructure in Frankfurt. Operational telemetry is not sent to a third-party observability vendor.

On the content of telemetry, this page describes the position precisely, because a published control that is not true would be a misrepresentation rather than a documentation error.

  • What is enforced automatically today: an automated check runs in continuous integration on every push and rejects any metric that carries an unbounded identifier as a label — a customer id, a user id, a record id. It fails the build; it is not advisory.
  • What is a written rule applied by review: since 23 July 2026 Atelye's engineering conventions prohibit any telemetry signal — log, trace, span attribute, span event or exception record — from carrying message content, media, AI prompts or completions, or a channel-scoped identifier such as a messaging-platform user id or a phone number. What is emitted instead is what the activity record emits: identifiers, correlation ids, channel names, state and timing. The rule binds all work from the date it was written and is applied through review of what the shared logging and tracing layers emit.
  • What does not exist yet: an automated test that proves the second rule the way the first one is proved. Building it is tracked work. Until it lands, that rule holds by discipline and review, and this page will not imply otherwise.

Backup and recovery

The application database runs continuous write-ahead-log archiving with daily full base backups, using pgBackRest, to encrypted object storage in the EU.

  • Recovery point objective: under 5 minutes. A write-ahead-log segment is forced and shipped at least every 60 seconds, so worst-case data loss is approximately the last unshipped segment — around a minute.
  • Point-in-time recovery window: 30 days. Recovery to an arbitrary instant is retained for the last 30 days.
  • Base backups: 90 days. Backups from 31 to 90 days old remain available as restore points at their own timestamps, without arbitrary point-in-time roll-forward.
  • Retention is enforced automatically at the end of every successful backup.

The restore procedure is scripted and has been proven end to end, not merely documented: the drill archives logs, takes a base backup, writes a point-in-time boundary between two marker records, restores into a throwaway cluster at that boundary, and asserts that the restored cluster contains the record from before the boundary and not the one from after it. The drill is re-run whenever the database image, the backup configuration, or the backup software changes. Note the cadence honestly: it is triggered by change, not by a calendar.

A destructive database migration is preceded by an on-demand full backup, which must report success before the migration is allowed to ship.

Change control

  • Every deployed artifact is pinned by content digest. Deployments reference an immutable image digest, never a moving tag.
  • The production host pulls; nothing pushes to it. It polls a private deployment repository on a timer and converges to whatever that repository says. There is no inbound deployment access, no webhook endpoint and no continuous-integration credential on the host.
  • Rollback is a revert. Reverting the commit in the deployment repository republishes the previous digest on the next poll.
  • Database migrations are forward-only. There are no down-migrations; an unwanted schema change is corrected by a new migration, so the applied history is always the real history.
  • Migration safety is a blocking gate. Every push runs an automated migration review (squawk) together with checks that a migration performs exactly one kind of change and that any destructive step carries an explicit written justification. A migration that fails the gate does not merge.
  • Type checking, linting, unit tests and the isolation and protected-test guards run on the same gate. Integration tests, authorisation policy tests and browser end-to-end tests run before a release is deployed.

Data deletion

What is deleted, what is retained and why, and the timelines involved are set out in section 12 of the Data Processing Addendum and on the Data Deletion page.

What Atelye does not have

Stated plainly, because you will find all of it out anyway, and because a page that lists only strengths is not an assessment:

  • No SOC 2 report. None has been undertaken.
  • No ISO 27001 certification.
  • No formal third-party penetration test. The platform has not been tested by an external security firm.
  • No third-party security attestation of any kind. No audit report, no certification, no independent assurance opinion is available, and none should be inferred from this page.
  • Single-operator access. Atelye is run by one person. That person holds administrative access to production systems. There is no separation of duties, no four-eyes approval on an administrative action, and no second person to detect misuse by the first. The controls that mitigate this are the ones described above — append-only activity records the operator cannot rewrite, pinned artifacts, a pull-based deploy path, and forward-only migrations — but they mitigate the risk, they do not remove it.
  • No at-rest encryption layer of Atelye's own over the live database, as stated under "Encryption".
  • No automated enforcement of the telemetry content rule, as stated under "Monitoring".

If any of these is disqualifying for you, it is better established now than after a procurement process.

Reporting a vulnerability

Report security issues to security@atelye.net. Please include enough detail to reproduce the issue, and give a reasonable period to remediate before disclosing publicly.

Commitment: Atelye acknowledges a report within 5 business days, gives an assessment and an expected remediation timeframe within 10 business days of acknowledgement, and tells the reporter when the fix has shipped.

Atelye will not pursue legal action over good-faith security research that stays within these bounds: no accessing, modifying or deleting data belonging to another customer beyond what is needed to demonstrate the issue; no degradation of the service; no social engineering of the operator or of suppliers; and no public disclosure before remediation or an agreed date.

There is no paid bug bounty programme.

Atelye notifies affected customers of a personal data breach without undue delay and in any event within 24 hours of becoming aware of it, per section 10 of the Data Processing Addendum.

How this page is kept true

This page is versioned and dated like every other document in this set, and superseded versions stay published at /legal/archive/security/<version>.

It is reviewed when a control changes and when the platform gains a capability. A control that stops being true is removed from this page in the same change that stops it being true — not at the next review.

Questions about anything here: security@atelye.net or privacy@atelye.net.