For us, security isn't a layer added later, it's the center of the architecture. Data isolation, identity separation and idempotent writes are baked into the lowest layer of the code — not a setting you can turn off, but how the system works.
PostgreSQL Row-Level Security is FORCE-enabled on every tenant-scoped table; the policy binds each row to the condition "tenant_id = current_setting('app.current_tenant')".
A query that comes in without the tenant context set returns zero rows — not even an error, just silently empty. Seeing one clinic's data with another clinic's session is architecturally impossible.
The cross-tenant isolation test is mandatorily green in continuous integration (CI); if it's red, the code is not merged.
Atomic writes
Transaction-local identity, atomic operation
The tenant identity is set at the very START of every write transaction and is valid only for that transaction; it never sticks to the connection pool and never leaks into other requests.
Multi-entity operations (invoice + line + payment + stock deduction) are atomic within a single transaction: either they're all written together, or none. No half-finished, inconsistent record is created.
Authorization
Role-based authorization (RBAC) enforced on the server
Authorization control is done at the API layer; the client or front end is never trusted. Hiding a menu in the browser is only about appearance — the real decision is on the server.
Core (Phase 1) mutations are protected by the admin, receptionist and doctor roles. Even if an unauthorized role calls the endpoint, the request is rejected.
Replay protection
Idempotent write — no double operation
Every write request carries an Idempotency-Key; the server-side lock blocks a second operation arriving with the same key.
Even when the network drops or the client resends the same request, no second charge or invoice is created. The result is always single and predictable.
Query security
SQL injection impossible — parameterized query
All database queries are fully parameterized statements generated at compile time with sqlc. There isn't a single line in the codebase that builds SQL by manually concatenating strings with user input.
This structurally eliminates the entire class of SQL injection attacks — it relies on how the query is constructed, not on "filtering" or "escaping" efforts.
Network surface
Loopback isolation — core closed to the outside
The core API listens only on 127.0.0.1 (loopback). Any attempt to bind to 0.0.0.0 or a public IP is rejected at startup; the core is never exposed to the internet under any circumstances.
The single public surface is a thin gateway (BFF): beyond sessions, rendering and CSRF it holds no business logic. The browser can never see or address the core API directly.
Identity & session
argon2id password, opaque session key
Passwords are hashed with the industry-standard, memory-hard argon2id algorithm; plaintext or weak hashes are never used.
Session keys are 32-byte crypto-random opaque values, stored in Redis and expiring via TTL. Only an HttpOnly + Secure + SameSite cookie is exposed to the browser; the key is never accessible to JavaScript.
Audit
Immutable audit log (append-only)
Important actions are written to the append-only audit log within the SAME transaction as the mutation that triggers them. If the operation is rolled back, the audit record isn't written either — the two are inseparable.
UPDATE/DELETE permission has been revoked from the audit table; records cannot be edited or deleted afterward. The past stays exactly as it was.
Privacy
Sensitive data masking
TCKN, passwords, session keys and health data never enter logs in any form. These fields are masked even in debug output.
When needed, sensitive fields are encrypted at the column level. Special-category health data is protected by rules built into the design from the start.
Money integrity
Money safety — numeric, reverse entry
Money is kept everywhere as numeric(14,2); money is never computed with float. Rounding happens at a single point, on the server — no kuruş drift or rounding difference occurs.
Cancellations and refunds are processed as a reverse entry rather than by deleting the record. The current balance isn't written by hand; it's always derived from the total of invoices and payments. The past isn't corrupted.
Transfer
Transport & browser security
All traffic is carried over HTTPS/TLS and HSTS is on; the browser establishes the connection only encrypted.
A strict Content Security Policy (CSP) blocks inline scripts; X-Frame-Options DENY prevents clickjacking, and X-Content-Type-Options nosniff prevents MIME-type confusion. Referrer-Policy and Permissions-Policy are enforced as well.
Important
Identity tier separation + separate management plane
The clinic user, the patient portal and the platform operator are SEPARATE identity tiers: separate headers, separate session scopes. A key from one tier does not work in another — authorization boundaries are strictly separated.
Platform and administration endpoints (tenant provisioning, billing, automation/add-on management, API key generation) are NOT part of the public API.
These functions run on a separate, internal control plane with a different identity pattern (a dedicated management key); it cannot be reached or discovered from the outside. This shrinks the attack surface significantly.
HOW IT WORKS
Single transaction, fail-closed isolation
The conceptual flow below shows how every write is locked to the tenant and stays idempotent. It's for illustration only.
# 1) The tenant context is established at the very START of the transaction (scoped to this tx only)
BEGIN;
SELECT set_config('app.current_tenant', :session_tenant_id, true);
# 2) Idempotency-Key lock — a second operation with the same key is never createdSELECT lock_idempotency(:idempotency_key);
# 3) Multi-entity write — everything atomic in a single txINSERT INTO invoices ...; # RLS: tenant_id covered automaticallyINSERT INTO payments ...;
INSERT INTO audit_log ...; # append-only, same tx
COMMIT;
# If context isn't set, the RLS policy returns no rows:SELECT * FROM patients; --> 0 row (not an error, silently empty)
Independently verifiable
Part of the transport security is externally verifiable: you can inspect your connection's HTTPS/TLS certificate and response security headers such as HSTS and the content security policy yourself from your browser's developer tools. Our claims rest on architectural controls, not on marketing slogans.
Have a question about the security architecture?
Our team is glad to discuss the technical details and your compliance requirements with you.