> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openinary.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> Auth model, API keys, upload validation, container users and incident response.

Openinary authenticates two ways: Better Auth sessions for the web dashboard, and
API keys for everything programmatic. Both live in one SQLite file. A key travels
as a bearer token:

```bash theme={null}
curl -H "Authorization: Bearer sk_your_key_here" \
  http://localhost:3000/upload
```

The auth layer is [Better Auth](https://www.better-auth.com) 1.6 on a SQLite backend.

## API keys

How you get the first one depends on the mode.

In fullstack mode, visit `/setup` to create your admin account, then `/api-keys` in
the dashboard to create the key.

In API-only mode the server generates a key on first startup and prints it to the
console. It is shown once and cannot be retrieved later, so copy it now:

```bash theme={null}
docker logs <container-id>
```

After that: store keys in environment variables, never in source code. Create one
key per service or environment. Set an expiration at creation time (`expiresIn`,
in seconds). Rotate regularly, and disable unused keys promptly.

Every route and its auth requirement is listed in the
[API Reference](https://docs.openinary.dev/api-reference/introduction.md), along with the rate limiting
defaults and options.

## Database

All auth data sits in a single SQLite file at `/app/data/auth.db`, configurable
via [`DB_PATH`](https://docs.openinary.dev/configuration/server#core). On startup, `scripts/secure-db.js`
sets its permissions to `600`, owner read/write only. It runs in both Docker
images, before the server starts.

| Table | Contents |
| - | - |
| `user` | Accounts, passwords scrypt-hashed |
| `session` | Web sessions |
| `apiKey` | API keys, hashed before storage |
| `account` | OAuth providers |
| `verification` | Email/phone verification tokens |

## Upload and delivery

[Uploads are validated by content](https://docs.openinary.dev/api-reference/files/upload.md), not by extension
or declared MIME type. The leading bytes must match the format the file claims to
be, so a `.jpg` carrying an HTML document never reaches storage. Delivery then
serves those bytes as they are, with `X-Content-Type-Options: nosniff` and a
content type taken from the shared media table rather than sniffed from the body.

That pairing is what closes the stored-XSS path on the public `/t/*` route, which
returns uploaded bytes untouched. Both halves are load-bearing: strict at the
door, no sniffing on the way out.

## Docker security

The two images differ here, and the difference matters when you pick one.

| Image | Runs as |
| - | - |
| `openinary/openinary-api` | `node`, non-root |
| `openinary/openinary` (full) | `root` |

The `full` image bundles nginx and supervisord alongside the app and sets no
`USER`, so every process in it runs as root. Application directories are owned by
`node`, but that does not change which user the processes run under. If running
non-root is a requirement for you, deploy the `api` image and put your own reverse
proxy in front of it.

Mount the `/app/data` volume with appropriate host permissions. Neither image
needs a privileged container or extra capabilities.

## Incident response

> [accordion] Compromised API key
>
> **1. Disable the key**
>
> Go to `/api-keys` in the dashboard and disable or delete the key immediately.
>
> **2. Review logs**
>
> `bash docker compose logs api | grep "api_key.success" `
>
> **3. Issue a replacement**
>
> Create a new key and update all services that used the compromised one.

> [accordion] Database integrity check
>
> ```bash theme={null}
> docker compose exec api sqlite3 /app/data/auth.db "PRAGMA integrity_check;"
> ```
>
> A healthy database returns `ok`. If it reports errors, restore from your most recent backup and restart.

## Additional resources

> [card] [Better Auth](https://www.better-auth.com)
>
> Official Better Auth documentation.

> [card] [API Key Plugin](https://www.better-auth.com/docs/plugins/api-key)
>
> Better Auth API Key plugin reference.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.