Installation Guide
freezr is much more fun with AI
If you are trying out freezr, please do give it access to ChatGPT or Claude. It's just so much more interesting when it has LLMs running. Once you've installed freezr, head to
/accounts/resources
to enter your API key.
How do you want to run freezr?
Setup / Settings
Once freezr is running it is configured in three places: a one-time first setup flow that creates the admin account, an Admin settings area for server-wide policy, and an Account settings area that each user has for their own data and credentials.
First-user registration
When a freezr server boots for the very first time, there is no admin account. The very first
HTTP visitor becomes the server's admin by completing a one-time first setup flow.
This flow is the same on a laptop running on localhost and on a freshly
deployed cloud instance.
Once the server is running, you will be redirected to /register/firstSetUp and
be asked to:
- Pick a user id and password for the admin account.
- Choose the file-system and database adapters this server will use by default (see the
adapters below). On localhost this can simply be
local+nedb, which needs no further credentials. - On a cloud / public server only: paste the one-time setup token
(
FREEZR_SETUP_TOKEN) that you set when deploying. On a laptop running withNODE_ENV=developmentthe token is skipped entirely. See the Cloud Servers section for details and the token format.
Submitting the form runs server-side checks on your chosen file system and database and marks the server as set up. The server will not accept first-setup again afterwards.
Admin settings
Available to the admin user at /admin/home. This is
where server-wide policy lives — user accounts, system preferences, and advanced
server functions.
| Function | Notes |
|---|---|
| List users | See every account on the server, their file system and database types and their storage use |
| Manage users | Edit, suspend, or remove existing user accounts - or install apps for them. |
| Register a new user | Create new users on the server (bypassing the public self-register flow.) |
| System preferences | Server-wide policy: whether users can self-register, default FS/DB, messaging rules, etc. |
| Cache preferences | Tune the in-memory caches to speed up cloud database access. |
| Review visit logs | Inspect the server's access logs. |
| Add a local microservice | Register a locally-running microservice (e.g. a Lambda-style function) for apps to call - this is dangerous!! |
| Set up as OAuth server | Make this freezr instance an OAuth identity provider for other sites. |
| Create cloud setup token | Generate a fresh FREEZR_SETUP_TOKEN for bootstrapping another cloud server. See below. |
Account settings
Available to every user at /account/settings.
This is where a user controls their own storage, credentials, and identity on the server.
| Area | Function | Notes |
|---|---|---|
| System configuration | Shows which FS, DB, and compute adapters the account is using, with links to edit parameters. | |
| Intelligence (LLMs) | Link to /account/resources to manage Claude / ChatGPT API keys. | |
| Resource usage | Per-app storage breakdown for the user. | |
| Logs | Per-user visit / access logs and errors. | |
| Profile picture | Upload / replace / delete the account's public profile image - used when you post publicly. | |
| Sharing / messaging | Block sending messages to non-contacts on other servers; block receiving messages from non-contacts. | |
| Change password | Standard old-password + new-password flow. | |
| Remove account | Permanently deletes the user's credentials and all their data on this server. | |
| Advanced settings | /account/reset — low-level account reset, for emergency use. |
Cloud Servers
freezr is a plain Node.js app so it can be installed anywhere that runs Node: a laptop, AWS (EC2, or Elastic Beanstalk), Azure App Service, or the likes of Heroku and Replit. There is no build step and few dependencies - any Node 18+ environment works out of the box.
On a laptop you typically run npm run dev and that's it. On a public host the
only thing that differs is that you set a few environment variables — most importantly
NODE_ENV, a first-setup token, a credential-encryption passphrase, and the
connection details for whatever file system and database you plan to use. All of that is
covered below.
Environment variables
freezr reads a small set of process.env variables at startup to decide which
adapters and ports to use, and to encrypt credentials at rest. On a laptop you almost never
need to set any of them (unless you want to test a cloud db or fs) — the defaults
(local file system, NeDB, port 3000) just work.
On a cloud host (AWS, Azure, etc.) you set them in whatever "Environment variables" /
"Config vars" panel the provider gives you. See below for details.
Core server
| Variable | Default | Notes |
|---|---|---|
| PORT | 3000 | HTTP port the server listens on. Most PaaS providers set this for you. |
| NODE_ENV | (unset) | Set to development on a laptop to skip the first-setup token check and enable verbose logging. Leave unset (or production) on a cloud host. See NODE_ENV. |
| FREEZR_SETUP_TOKEN | (required on cloud) | One-time token authorising the first-user registration. See Set Up Token. |
The specific variables for each adapter (file system, database) are listed under the adapter that uses them in the Resource adapters section below.
NODE_ENV
NODE_ENV controls the two modes freezr runs in:
development— used bynpm run dev. Skips the first-setup token check (so you can just visit/register/firstSetUpon a laptop and become the admin), enables verbose logging, and relaxes a few other safety checks. Only appropriate onlocalhost.production(or unset) — used bynpm startand by every PaaS host. The first-setup flow refuses to run unless the caller presents a validFREEZR_SETUP_TOKEN, preventing a random visitor from claiming the admin account before you do. Most cloud providers setNODE_ENV=productionfor you automatically.
Set Up Token — FREEZR_SETUP_TOKEN
The FREEZR_SETUP_TOKEN used by the cloud first-setup flow has the format
<secret>.<YYYY-MM-DD> — a random 24-byte hex secret followed by an
ISO expiry date (default 1 day out). After that date the token is refused with
auth-setupTokenExpired. Generate one with createSetupToken() (or
from the admin UI at /admin/createCloudToken). Tokens are compared with a
constant-time timingSafeEqual. Typical workflow: deploy with the variable set,
finish the form, and leave it be — the server will not accept first-setup
again regardless, but it will require a value in the variable if / when the server restarts.
Credential encryption — FREEZR_ENV_KEY
freezr stores the FS, DB, and LLM credentials it needs at startup inside
freezr_environment.js and in the info.freezr.admin.params DB
record. If you set FREEZR_ENV_KEY, those credential blobs are transparently
encrypted with AES-256-GCM before being written and decrypted on read, so that anyone who
gets hold of the file / DB record cannot read your Dropbox tokens, S3 keys, Mongo
connection strings, or LLM API keys.
| Variable | Details |
|---|---|
| FREEZR_ENV_KEY |
≥ 16 chars (recommended 32+ random)
Passphrase that is SHA-256-hashed to derive the 32-byte AES key. If set, all
|
Database unification strategies — DB_UNIFICATION
MongoDB-based cloud hosts (Atlas, Cosmos DB, etc.) often charge per database or per
collection, or cap how many you can create. By default freezr gives every user their own
database and every app/table its own collection, which is tidy but explodes on managed
hosts. DB_UNIFICATION (mirrored at the preferences level as
dbUnificationStrategy) tells the Mongo adapter to consolidate writes. It has
three valid values:
| Value | Details |
|---|---|
| db |
one DB per user, one collection per app_name.table
Default. No unification. Each user's data lives in a database named after their
userId (or in |
| allButAdmin |
one DB + one collection for regular users; admin/system users keep their own
Recommended default for small cloud deployments. Regular user records are all stored
in the unified DB ( |
| all |
everyone — including admin/system — shares one DB + one collection
Most aggressive. Every record for every user and every app lives in the same
|
Under a unified strategy, freezr rewrites record _ids to include the owner,
stamps each document with __owner and __appTable, and filters
every read / update / delete by those fields so apps still only see their own rows.
UNIFIED_DB_NAME (above) controls the logical database name; the collection
name is always allUserAppData.
Important: DB_UNIFICATION is not safely changeable
after data has been written — the admin service refuses to update
dbUnificationStrategy once it is set, and the startup sequence throws
db process unification mismatch if the env var disagrees with what's stored
in the admin prefs. Pick a strategy at first setup and keep it.
Resource adapters
freezr treats storage, compute, and LLM providers as pluggable adapters. Each user chooses their own set at account-creation time. The server routes every read and write through the configured adapter; no user data flows through a central freezr service.
File-system adapters
An adapter implements a small interface: read, write, delete, list, getReadStream. Shipped connectors:
| Adapter | File | Notes |
|---|---|---|
| local | dbfs_local.mjs | Default. Files stored under users_freezr/<user>/files/. Good for self-hosted servers. |
| aws | dbfs_aws.mjs | S3. Uses the user's own AWS credentials. |
| azure | dbfs_azure.mjs | Azure Blob Storage. |
| dropbox | dbfs_dropbox.mjs | Dropbox via OAuth. |
| googleDrive | dbfs_googleDrive.mjs | Google Drive via OAuth. |
On a cloud install you can bootstrap the file-system adapter from environment variables.
The top-level FREEZR_FS selects the adapter; the remaining variables supply
its credentials. If FREEZR_FS is unset (or set to local), freezr
falls back to the local disk under users_freezr/.
| Variable | Used when | Notes |
|---|---|---|
| FREEZR_FS | always | One of dropbox, aws, azure, or local (default). |
| FS_TOKEN | FREEZR_FS=dropbox | Dropbox access token. |
| FS_REFRESH_TOKEN | FREEZR_FS=dropbox | Dropbox refresh token (preferred over a short-lived access token). |
| FS_CLIENTID | FREEZR_FS=dropbox | Dropbox OAuth client id. |
| FS_REDIR | FREEZR_FS=dropbox | OAuth redirect URI. |
| FS_C_CHALL / FS_C_VER | FREEZR_FS=dropbox | PKCE code challenge and verifier. |
| FS_ACCESS_KEY_ID | FREEZR_FS=aws | AWS access key id. |
| FS_SECRET_ACCESS_KEY | FREEZR_FS=aws or azure | AWS secret access key / Azure secret access key. |
| FS_BUCKET | FREEZR_FS=aws | S3 bucket name (if blank, freezr can create one with the rights on the key). |
| FS_STORAGE_ACCOUNT_NAME | FREEZR_FS=azure | Azure storage account. Mandatory for the azure adapter. |
| FS_MS_CONNECTION_STRING | FREEZR_FS=azure | Connection string. Only needed if the server is not running on the same Azure backend. |
| FS_CONTAINER_NAME | FREEZR_FS=azure | Azure container name. |
| REPL_ID | auto | Auto-detected. Forces local storage with choice: 'replit'. |
Google Drive has no environment-variable bootstrap — it is configured per-user
through the in-app OAuth flow at /accounts/resources.
Database adapters
An adapter implements query, create, read, update, delete, count, plus index / schema helpers. Collection names are scoped per app (<app_id>.<table>). Shipped connectors:
| Adapter | File | Notes |
|---|---|---|
| nedb | dbApi_nedb.mjs | Embedded, file-backed. Zero-config default — used for dev and small deployments. |
| mongodb | dbApi_mongodb.mjs | Full MongoDB. Dangerous operators ($where, $function, $accumulator, $expr) are blocked server-side before the query reaches the driver. |
Queries pass through dsManager and userDsMgr before hitting the adapter — this is where per-record size limits (2 MB), rate limiting, regex safety, and per-user caching are enforced.
Like the file-system, the default database can be bootstrapped from environment variables.
The top-level FREEZR_DB selects the adapter and the remaining variables
supply its connection details. If FREEZR_DB is unset, freezr auto-detects
what is available (a local mongo on 27017 or NeDB on disk).
Even though NeDb is not meant to be used for large implementations, the fact is that your personal server collections won't run into millions of records too quickly, so NeDb is a great way to start.. and perhaps continue - NeDb has managed 10,000+ records per file without any prolems.
| Variable | Used when | Notes |
|---|---|---|
| FREEZR_DB | always | One of nedb, mongodb, or cosmosformongostring. |
| MONGO_STR | FREEZR_DB=mongodb or cosmosformongostring | Full MongoDB / Cosmos-for-Mongo connection string (e.g. from Atlas). Takes precedence over the host/user/pass variables below. |
| DB_HOST | FREEZR_DB=mongodb (no MONGO_STR) | MongoDB host. |
| DB_PORT | FREEZR_DB=mongodb (no MONGO_STR) | MongoDB port. |
| DB_USER | FREEZR_DB=mongodb (no MONGO_STR) | MongoDB user. |
| DB_PASS | FREEZR_DB=mongodb (no MONGO_STR) | MongoDB password. |
| ADD_AUTH | FREEZR_DB=mongodb | Truthy to append auth info to the connection string. Default false. |
| UNIFIED_DB_NAME | FREEZR_DB=mongodb or cosmosformongostring | Name of the logical database inside the cluster. Defaults to freezrDb (mongo) or freezr (cosmos). Also used as the shared DB under a unified strategy. |
| DB_UNIFICATION | FREEZR_DB=mongodb | Cloud consolidation strategy: db (default, one DB per user), allButAdmin, or all. See Database unification strategies. Must match the admin-saved dbUnificationStrategy and is fixed after first setup. |
Some databases require manual indexing. If you notice slow queries after adding data, check that the relevant fields are indexed for your chosen adapter.
If using Azure's Cosmos specifically, you would need to build composite index of _id and _date_modified (in mongo shell: use freezr then db.allUserAppData.createIndex({ "_date_modified": -1, "_id": -1 }, { name: "dateModified_id" }))
and also db.allUserAppData.createIndex({ __owner: 1, __appTable: 1, isPublic: 1, doNotList: 1, _date_published: -1 })
You could also do a catchall db.allUserAppData.createIndex({ "$**": 1 }) .. or
LLM connectors
LLM connectors normalise provider-specific protocols behind freezr.llm.ask and freezr.llm.generateImage. The user supplies their own API keys in their account settings; freezr never proxies through a central key. Streaming is handled via server-sent events regardless of provider.
| Provider | Notes |
|---|---|
| Claude | Anthropic API. Supports extended thinking (thinking: true or { budget_tokens }). Images: SVG generated then server-side rasterised to PNG. |
| ChatGPT | OpenAI API. Supports reasoning effort (thinking: { effort: 'low'|'medium'|'high' }) and auto-selects o-series models. Images via gpt-image. |
LLM keys are not configured via process.env. Each user enters their
own Claude / ChatGPT key at /accounts/resources
and it is stored encrypted (with FREEZR_ENV_KEY, when set)
in their own lmParams record.