The write path

The register the browser cannot rewrite.

Balances are decided on the server. Every change goes through one authenticated function, and the audit trail is append-only. This page shows exactly how that works — and what it does not claim.

Server-authoritative Read-only client Append-only audit Reviewed October 2026
One request, six checks

What happens when someone logs a dispense.

The page you are reading can read the register. It cannot decide a balance or write a row.

01Google sign-inThe account must be on the clinic’s member list. Signing in alone does not grant access.
02Role capabilityThe server checks the member’s role before the action is allowed. The browser cannot promote itself.
03PIN shift sessionThe staff member on shift is identified by a hashed PIN with lockout and an expiring session.
04Transaction + auditOnly changed documents are written, the revision is bumped, and an append-only audit row is added.

The browser is a read model.

Firestore security rules let clinic members read the register and deny every client write. There is no “save” path in the browser that can change a bottle balance, a log row, or an audit entry.

The only write path is a callable Cloud Function. It checks authentication, membership, the role’s capability, the staff PIN session, a rate limit, and an idempotency key before it loads the affected rows and hands them to the shared business-rule engine. The engine returns the mutated register; only the documents that actually changed are committed.

That architecture is the reason a balance can be trusted: it is computed from the register on the server, not typed into a field on a web page.

Corrections, not erasures.

Rows are never hard-deleted. Voiding an entry writes a reversing correction row and flags the original. The original stays visible in the trail, and the correction explains what changed. This is how a register should handle a mistake — not by making the mistake disappear.

Why it matters: an auditor can see both the entry and the correction. A system that silently overwrites a row gives you a cleaner screen and a weaker record.

Secrets stay off the client.

PIN hashes, staff sessions, idempotency records, and rate-limit records are denied to browser clients. The client never receives a PIN hash or a session secret; it receives the result of an authorized action.

Least privilege

Five roles. One matrix. Enforced on the server.

The browser may hide a button, but the matrix is what the function enforces. A viewer cannot log a dispense by calling the API directly.

Role capabilities

Source: shared/authz.ts · enforced by the Cloud Function

Server-side
Capabilities granted to each clinic role.
RoleReadLogVoidReceiveFileMembersSettingsOwner
Owner✓✓✓✓✓✓✓✓
Clinic admin✓✓✓✓✓✓✓—
Pharmacist✓✓✓✓✓———
Technician✓✓——————
Read-only✓———————

“Log” covers dispense, waste, counts, and incident drafts. “File” covers PMP packets and Form 106 paperwork. Only an owner can change another member’s role or transfer ownership.

The limits

What we do not claim.

A trust page that only lists strengths is a sales page. These are the boundaries.

Not legal advice

The register supports recordkeeping. It does not decide whether your clinic is compliant, whether a loss is “significant,” or which state rules apply.

Not DEA-approved

DEA does not approve recordkeeping software. We do not display an approval badge, and no page on this site claims one.

No borrowed security badges

We do not claim SOC 2, HIPAA, or “bank-level” security. We describe the actual controls: server writes, roles, PIN sessions, audit, and export.

Your record stays portable.

The register can be filtered and downloaded as a portable record. That matters for two reasons: an inspection may ask for a period, and a clinic should never be trapped in a vendor to read its own history.

Attachments — invoices, Form 222 copies, PMP files, incident evidence — are stored server-side and readable by clinic members. Storage writes also go through the function, not the browser.

What we will tell you if it changes.

If the architecture, subprocessors, or access model changes materially, this page and the privacy policy will be updated before the change is described anywhere else. A security page that quietly goes stale is worse than no page.

Still skeptical?

Good. Read the answers with the limits attached.

The FAQ covers DEA approval, binder replacement, PMP filing, retention, and access — in plain language.

Read the FAQ
See the model, not just the copy

Log one entry. Watch the trail.

Two minutes, sample data only, no account and nothing saved.

Already have an account? Clinic sign inPlans from $39/mo