Skip to content
Menu

Ordence

Trust and security

This page is written for the person who has to sign something — the owner, their chartered accountant, their banker. Every claim on it names the part of the system that implements it, so it can be checked rather than believed. The last section lists what we do not have.

What is in place today

In place today

One business cannot read another business's rows

Separation between businesses is enforced by PostgreSQL itself, not by application code remembering to add a filter. Every table that carries a business identifier has row-level security enabled and FORCED. The distinction matters: enabling row-level security alone does not apply it to the table's owner, and this application connects to the database as the owner — so enabling without forcing would be decoration.

The reason this is enforced in the database rather than in code is that application filters are only as good as the last developer who remembered one. A missed WHERE clause in a single query is enough to show one company another company's ledger.

A check runs on every build that reads the live database and, for every table carrying a business identifier, asserts four things: that row-level security is on, that it is forced, that a policy actually references the current business, and that platform-wide support access can read but never write. There is no threshold and no sampling. One unprotected table fails the build.

Where to check this

  • scripts/check-rls-coverage.mjs — the build gate
  • SQL-FILES/0001_rls_and_audit_guard.sql — the original policies

In place today

The audit trail can prove it was not edited

Audit records are append-only: a database trigger refuses any UPDATE or DELETE against them, and the access policies refuse the connection. That stops the application from rewriting history.

It does not, by itself, stop somebody with full database credentials, who could disable the trigger, edit a row, and switch the trigger back on. So each audit row also carries a SHA-256 digest of its own contents and of the row before it, forming a chain. Altering any historical row changes its digest and breaks every link after it, which makes the alteration detectable rather than merely forbidden.

Being honest about the limit: a hash chain proves tampering happened. It does not prevent it, and it cannot recover the original text. Audit rows written before the chain was introduced sit outside it and are marked as such rather than quietly presented as chained.

Where to check this

  • SQL-FILES/0081_audit_hash_chain.sql — the chain
  • server/audit.ts — where each link is computed on write

In place today

A closed accounting period stays closed

When a period is closed, a database trigger refuses any transaction dated inside it. The refusal is on the document date, not the date the row was inserted — backdating is precisely the move the control exists to stop.

This is enforced at the database level for the same reason as the isolation above: a period close that only the user interface respects is not a period close. Reopening a period is possible, but it is a deliberate act by somebody permitted to do it, and it is recorded.

For a CA this is the difference between numbers that were signed off and numbers that were signed off and then quietly moved.

Where to check this

  • SQL-FILES/0073_period_lock_and_reorder.sql — the guard trigger

In place today

What our support staff may do inside your workspace

Support access to a customer workspace is called impersonation, and it is constrained by a policy held in one file that the banner, the server-side gate, the database checks and the tests all read from.

Access is read-only by default. Write access exists only where the customer has consented — either standing consent recorded by a workspace owner, revocable at any moment, or consent given by an admin for one specific incident.

Every session, in every mode, expires after thirty minutes. That is a hard ceiling applied on read as well as on write, so a session already in progress cannot outlive it. One support engineer may hold one session at a time.

Where no consent exists and the situation is urgent, an engineer may open a break-glass session. It is read-only, it is fifteen minutes, it requires a written justification, and the workspace's owners are notified immediately rather than in a monthly report. The reasoning was that a customer's inability to answer the phone should reduce what we may do, not increase it.

Some actions are refused even with full consent — changing roles, issuing invitations, and anything else that would outlive the session or that the customer could not undo themselves.

Where to check this

  • lib/platform/impersonation-policy.ts — the policy, with its reasoning written out

In place today

Credentials for your other systems are encrypted before storage

When you connect Ordence to another system, the credential you hand over is encrypted with AES-256-GCM before it reaches the database. What is stored is ciphertext, an initialisation vector, a masked display value, and the name of the key used — never the key itself, which is held in the deployment environment and not in the database.

Searchable fields use a keyed blind index rather than the plaintext, so a credential can be looked up without being stored in a readable form. Every read of a secret is written to an access log that no application role may delete.

Being precise about scope: this describes secrets held in the vault. It is not a claim that every field in the product is individually encrypted.

Where to check this

  • server/vault/crypto.ts — the encryption
  • server/vault/secrets.ts — storage and access logging

In place today

The browser-side controls

Pages are served with a Content-Security-Policy built per request around a fresh nonce. It contains no unsafe-inline in its script sources and it sets a base URI — the three ways a policy of this kind becomes decoration are each covered by a test that is phrased as the failure rather than the feature.

Session cookies and device-local preferences are the only things stored in your browser; the cookie banner describes that scope rather than a generic one.

Where to check this

  • lib/security/csp.ts and tests/ui/csp.test.ts

What we do not have yet

Stated plainly, because you will find out anyway, and finding out later is worse for both of us.

Not yet

We hold no SOC 2 report and no ISO 27001 certificate.
We have not been through either audit. If your policy requires one, we would rather you knew now than after implementation. What we can offer instead is the specific evidence on this page, and answers to a security questionnaire that name files rather than adjectives.

Not yet

We have no published penetration test.
No third-party test has been commissioned. Internal checks — the build gates described above, plus tests for cross-tenant access and injection — run on every change, but an internal check is not an independent one and we will not present it as one.

Not yet

We do not publish a formal uptime commitment.
There is no contractual availability figure. Ask us for the actual operating history rather than a number written into a page.

Not yet

Customer-managed encryption keys are not available.
Vault keys are held by Ordence in the deployment environment. If your organisation requires that it hold the key, we cannot meet that today.

Not yet

Data residency is answered on request, not asserted here.
Ordence runs on managed infrastructure — Neon for the database, Railway for the application — and the storage region is a property of that infrastructure. We would rather tell you the current region in writing, and tell you when it changes, than print a region on a marketing page that nobody updates. Ask, and we will answer specifically.

Not yet

Encryption in transit and at rest is provided by our infrastructure vendors.
Traffic is served over HTTPS, and our database provider Neon encrypts stored data. Those are properties of Neon and of the hosting platform, described in their documentation — we attribute them rather than restating them as our own engineering.

Reporting something, or asking for more

If you believe you have found a security problem in Ordence, write to security@ordence.com. Our machine-readable contact details follow RFC 9116 and are published at /security.txt.

For a security questionnaire, a data-processing agreement, or the current storage region, write to the same address. We answer with specifics.

Trust & security — Ordence