Encore
Trust

Receipts, not badges

Every claim on this page is true of the code that is running right now - not a roadmap, not a marketing layer. Here is how your work and your clients' moments are protected, and a plain list of what we don't claim.

SHA-256 audit chainsOne access gateNo public buckets2FA on every plan

A real chain, not a diagram

The events are invented - publishing a client's actual log would be its own kind of failure. The hashes are not: each is a genuine SHA-256 of its row chained to the one before, built by the same construction that hashes a production event. Change one character in any row and every hash below it stops deriving. That is the whole claim, and you can check it.

Verify an audit bundle yourself →

01 · Receipts

Tamper-evident by construction.

sha256(previous hash + event), head countersigned by an RFC 3161 timestamp authority

Every view, download, and share in a delivered gallery is written to an append-only audit chain. Each event's hash is the SHA-256 of the previous event's hash plus the event itself, so the log can't be quietly edited after the fact - alter or remove one row and the chain breaks at exactly that row. Verification re-walks the whole chain and re-computes every hash.

The same chain carries account-level security events - password changes, 2FA on or off, recovery-code rotation, new devices - so "tamper-evident receipts" covers the account, not just the gallery. Even platform-level trust and safety review of a studio's content is written to a chain; cross-tenant access is never silent.

A chain on its own proves the rows are consistent with each other. It cannot prove when they were written, because every input to it is ours, and in a real dispute the objection is never "your hashes don't match" - it is "that is your own database, you could have typed anything into it". So the chain head, which is one hash covering every event behind it, is sent to an independent RFC 3161 timestamp authority nightly and on demand. What comes back is a token that authority signed with its own key, attesting that this exact hash existed at that instant. We keep it verbatim.

You can hand the whole thing to someone who has no reason to trust either of us. The evidence bundle is one file carrying every hashed field plus the timestamp tokens, and /verify re-hashes it in the reader's own browser with no account and no request to our servers. The authority's signature is checkable separately with stock openssl against its published certificate, which is the point: we are not asking anyone to take our word for the part that matters.

02 · Isolation

One gate on every byte.

shared access gate, re-checked per request - denials are 404, not 403

Every route that serves gallery content - the page itself, per-photo downloads, bulk zips, gallery audio, the live photo feed - passes through one shared access gate before anything leaves storage. The gate re-checks, in order: moderation takedown, studio suspension, expiry, private visibility, and password lock. One helper, one gate, no side doors that keep serving after a gallery goes dark.

Denials return 404, not 403: a private or taken-down gallery is indistinguishable from one that never existed. Password unlock cookies are HMAC-signed over the gallery id plus the current password hash - a cookie issued for one gallery can't open another, a hand-forged cookie fails verification, and rotating the password invalidates every cookie issued before it.

03 · Storage

Private bucket, expiring links.

Cloudflare R2, per-studio key prefixes, presigned GET/PUT only

Originals live in Cloudflare R2 in a private bucket, keyed per studio. There is no public bucket listing; content is served through presigned URLs that expire - a photo download link is signed for minutes, not forever. Uploads work the same way in reverse: the browser gets a short-lived presigned PUT and writes directly to storage, so large files never transit our application servers.

Encryption at rest for stored files is provided by Cloudflare as part of R2 - that's their work, and we credit it to them rather than dressing it up as ours.

04 · Accounts

2FA on every plan.

bcrypt passwords · TOTP + single-use recovery codes · session versioning

Passwords are hashed with bcrypt - never stored in a recoverable form. TOTP two-factor authentication and eight single-use recovery codes are available on every plan, including free; security is not an upsell. Recovery codes are stored as SHA-256 hashes and shown exactly once.

Sensitive changes - password change, 2FA disable, recovery-code rotation - bump a per-user session version that invalidates every other device's session and fire a real-time alert email. Sign in with Apple and Sign in with Google are supported alongside email and password.

05 · API

Tokens we can't leak.

SHA-256 hash storage, prefix display, instant revocation

API bearer tokens are stored as SHA-256 hashes - the same model as GitHub and Stripe keys. A leaked database row does not yield usable tokens, by design: the plaintext is shown once at creation and can't be recovered from what we store. The UI lists only a short display prefix, enough to tell tokens apart without leaking entropy.

Every token is scoped to a single studio, can carry an expiry, and can be revoked instantly. API access is a plan feature. Failed authentication returns a bare 401 with no hint about why - an attacker probing should learn nothing.

06 · Billing

Card data never touches Encore.

Stripe on web, Apple App Store on iOS - we store state, not credentials

Web subscriptions run through Stripe; iOS subscriptions run through Apple's App Store. Payment credentials live with those processors, never on our servers or in our database. We store subscription state - which plan, which interval, active or not - and nothing that could charge a card.

07 · Content safety

A real moderation and takedown program.

public report intake · DMCA agent · review console · enforced at the gate

Anyone can report a gallery without creating an account - a victim of abusive imagery shouldn't need a login to be heard. Copyright claims have a dedicated DMCA intake that notifies our agent and the affected studio. Reports land in a review console where a human acts on them.

Enforcement has teeth: a takedown darkens the gallery for everyone, including its owner, and a studio suspension darkens the entire tenant. Both are checked inside the same access gate every serving route uses, so taken-down content can't keep leaking through a stale link or a cached photo id.

08 · Ownership

Your frames, your receipts, your exit.

30-day trash window → hard purge · CSV audit export

Photographers own their work. Deleting a gallery moves it to trash, where it's recoverable for 30 days; after that a purge job hard-deletes the records and the stored files. Gone means gone.

Your audit log is exportable as CSV - the full hash-chained history of who viewed, downloaded, and shared what, in a format you can hand to a lawyer, a publication, or a licensing dispute. Your receipts leave with you.

The other half of honesty

What we don't claim.

Trust pages tend to be badge soup. Here is ours without the soup - the certifications we don't hold yet, and the protections that belong to our infrastructure providers rather than to us.

SOC 2 / ISO 27001

Not certified. We're a small, photographer-owned platform and we'd rather tell you that plainly than rent a badge. The mechanisms above are the real substance those audits check for; the paperwork comes when the business justifies it.

Third-party penetration test

No certificate to show you yet. The codebase gets internal security review, and the disclosure address below is read by the people who wrote the code.

Encryption at rest

Provided by our infrastructure: Cloudflare R2 encrypts stored files and Neon encrypts the Postgres database as part of their platforms. That's their engineering - we credit it to them instead of claiming it as our own layer.

That a verified log means the events are true

It does not, and no hash can make it so. Verification proves the rows have not been edited, reordered, or removed since they were written, and a timestamp token proves they existed by a given instant. Whether what was recorded was accurate in the first place is a separate question, and any platform telling you otherwise is overselling. We also cannot prove an event was written at all - the chain catches changes to the record, not gaps in it.

That we verify the timestamp authority's signature for you

We check that each token was issued over our own chain head, which is what stops a valid token being passed off as covering data it has nothing to do with. We do not validate the authority's signature or its certificate chain in our code, because doing that badly would be worse than not doing it at all. Both the token and the openssl command that checks it properly ship inside every evidence bundle.

Formal SLA

No contractual uptime guarantee today. Live status is public at /status - Postgres, R2, and audit-log reachability, checked at the moment you load it. We do not publish an uptime history yet, so treat it as a live check and not a record.

DPA / procurement paperwork

Need a data processing agreement or a vendor security questionnaire filled out? Email us at security@encore.photo and a human will handle it.

Responsible disclosure

Found something?

Report vulnerabilities to the address below. Reports go straight to the people who wrote the code, we read every one, and we won't come after good-faith research. We don't run a paid bounty program today - we'll say so plainly if that changes.

security@encore.photo