Security Overview

How Invy separates organizations, protects accounts, and records what happened.

Invy holds your asset register: what you own, where it is, who has it. This page describes how that data is protected, in enough detail that you can judge it rather than take our word for it.

Where something is not yet in place, it says so. A security page that only lists strengths is not useful to anyone making a decision.

Tenant isolation

Invy is multi-tenant: many organizations share one system, and separation is enforced in software rather than by giving each customer a separate database.

Every organization’s data is separated by row-level scoping. Each tenant-scoped table carries an organization_id column, and every query filters on it.

The organization identifier is derived server-side from the authenticated session. It is never accepted from client input — there is no request parameter, form field, or URL path that changes which organization’s data a request sees. Changing what you send to Invy cannot change whose data comes back.

Access fails closed. If organization context is missing, the query returns an error rather than unscoped results, so a coding mistake produces a visible failure instead of a silent leak.

The honest limit: this is enforced in the application layer. Invy runs on SQLite, which has no database-level row security, so isolation comes from scoped queries and parameterized statements rather than from policies the database enforces on its own. A database that enforced this itself would be one more layer of defence. We have audited the query layer against this standard and every unscoped query is a deliberate, access-controlled exception — platform administration, authentication, and scheduled maintenance — but the guarantee rests on that discipline being maintained.

Public asset labels

QR labels printed from Invy encode a public URL, so a unit can be identified with a phone camera and no app installed. That link is deliberately readable by anyone holding the physical label.

It gives away very little. Scanned by someone signed in to the owning organization, it opens the unit in Invy. Scanned by anyone else, it shows only the organization name and the short ID — what is already printed on the sticker in their hand.

Scanning a label belonging to another organization returns exactly the same response as scanning a label that does not exist. The two cannot be told apart, so the endpoint cannot be used to confirm whether a given code is real. It is also rate-limited, and asset identifiers are never written to logs on this path, because those logs would pair identifiers with visitor IP addresses.

Accounts and sessions

Passwords are hashed with bcrypt. Invy never stores a password it can read, and cannot recover one for you.

Sessions use a signed token in a cookie that JavaScript cannot read (HttpOnly), is not sent on cross-site requests (SameSite=Lax), and is restricted to HTTPS in production (Secure). Sessions last 7 days by default.

Every form submission carries a CSRF token, so another site cannot cause your browser to take actions in Invy on your behalf.

Sign-in and password-reset endpoints are rate-limited per IP address to blunt password guessing. Where configured, Cloudflare Turnstile adds bot protection on authentication pages.

Permissions

Access within an organization is controlled by granular permissions rather than a single admin flag — inventory, locations, borrowings, maintenance, reports, exports, audit logs, team management, and settings are each gated separately, so people can be given exactly the access their job needs.

Platform administration is separated the same way. Provider staff hold scoped capabilities — read-only console access, billing, tenant management, backups, platform settings — rather than one blanket super-admin switch, so an account that can read platform health cannot necessarily change a tenant’s data.

The audit trail

Invy records what happened — who moved which unit, who lent what to whom, who changed a setting — in an append-only event log.

Append-only is enforced by the database itself. Update and delete triggers on the events table abort the transaction outright. The history cannot be quietly rewritten from the application, because the application is not permitted to rewrite it.

Impersonation by platform support staff is recorded as an event like any other action, and is reviewable.

Your data, and getting it out

You can export your entire organization — records and attachments — as a portable archive at any time, without asking us. Before restoring an archive, you can preview exactly what would change.

This matters for a reason beyond convenience: it means leaving Invy does not require our cooperation.

Scheduled backups are retained for 14 days.

Deleting your account removes your personal information and purges your uploaded files from storage. Records that other data depends on are anonymized rather than erased, so shared history — who moved an asset three years ago — stays coherent for the organization that still relies on it. We describe this as anonymization rather than deletion because that is what it is.

In transit and in the browser

Traffic is served over HTTPS, and Invy sends HTTP Strict Transport Security (HSTS) with a one-year lifetime covering subdomains. After a first visit, your browser refuses to talk to Invy over plain HTTP at all, which closes the “SSL stripping” attack where someone on the same network keeps a session on unencrypted HTTP.

Invy sets a Content Security Policy with per-request nonces, which limits what can execute in your browser and narrows the impact of a content-injection bug. It also sets X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy.

The cookies Invy sets, and the flags protecting each one, are listed in full in the Cookie Policy.

Who else touches your data

Invy is self-hosted on infrastructure we control. Files and attachments are stored on our own object storage rather than handed to a third-party storage provider, and PDF generation runs on our own infrastructure.

Three of the services below are optional and only active when configured. A self-hosted deployment can run without them.

Service Purpose Always used?
Hostinger Server hosting — application, database, and file storage Yes
Resend Transactional email — invitations, password resets, notifications Yes
Polar Payments and subscription billing Yes
Sentry / GlitchTip Error monitoring Only if configured
Cloudflare Turnstile bot protection on authentication pages Only if configured
Umami Cookieless usage analytics Only if configured

We do not sell your data, and we do not share it with advertising networks. The two browser-facing services above — Turnstile and Umami — are covered in more detail in the Cookie Policy.

Reporting a security problem

If you believe you have found a vulnerability, please report it privately rather than publicly, and give us a reasonable chance to fix it. We will not pursue action against anyone who reports a genuine issue in good faith and does not access or destroy other people’s data while investigating.

This page describes Invy as currently built and is updated as the software changes.

Loading...
Processing