Technical Aspects

freezr is an open-source open-protocol personal server built on Node.js. It provides a thin layer of functionality - providing identity, permissions, and data routing — connecting apps to the data owner's files, database, compute, and LLM resources!

Architecture (Protocols)

At its core, a freezr app is just a static front-end bundle — HTML, CSS, ES-module JavaScript — served from the user's freezr server. To ensure security, app logic runs in the browser and no server-side code can run directly on freezr. Data, files, permissions, messaging, LLM calls, and serverless invocations go through the freezr JavaScript API, which is auto-injected into every app page as a global object. Server-side code can run on serverless instances connected to each user and given the appropriate permisisonsas described below.

The server is organised into three layers of protocols:

LayerResponsibility
api The freezr API uses HTTP endpoints under /ceps/* (standard) and /feps/* (freezr extensions). Primarily handles authentication, and permission checks for data and app access. See below for more details
rendering Serves app pages at /apps/<app_id>/<page>. Wraps inner-body HTML with the document skeleton, injects freezrApiV2.js, applies CSP headers with per-request script nonces, and issues path-scoped app tokens.
adapters Pluggable resource layer: file-system, database, serverless compute, and LLM connectors. File systems can be local or on the clkoud. Database adapters need to be mongoDb adaptible. See here for more details

Every API call carries two identities: the logged-in user (a session on the global / cookie) and the calling app (a path-scoped bearer token, issued when the user visits the app's page). Permission checks run against the pair — an app can only touch data it is authorised to touch for the current user. See security below for more details or review the href="/security">security deepdive.

freezr is CEPS-compatible. Any app that speaks the Common Endpoints specification works with freezr without modification. feps, or freezr's specific end points extned the functionality of CEPS.
architecture / security

Security model

freezr runs third-party apps on the same origin as the user's own data. Isolation is built from four complementary mechanisms: path-scoped cookies, token-bound API calls, a strict Content Security Policy,< and per-user permission checks on every request. Below is a summary of the security model - a deeper dive into security can be found here.

Also of note: pubicly accessible pages do NOT use cookies and have less strict CSP.

Isolation model

The core trick: each app is served from its own URL path (/apps/<app_id>/*) and receives a bearer token in a path-scoped cookie. The browser only delivers that cookie on requests inside the app's path, so one app literally cannot see another app's token. The API then ties each token to both the app and the user, so a stolen token from app A is useless against app B's data.

Set-Cookie: app_token_<user_id>=<random>;
    Path=/apps/com.you.notes;
    Secure; SameSite=Strict
// httpOnly=false — JavaScript must read this to build the
// Authorization header; see the path-scope protection above

Additional mechanisms layered on top:

  • Session cookiehttpOnly, SameSite=Strict, 30-day TTL. JavaScript cannot read or forge it.
  • Sec-Fetch-Mode check — server-rendered pages with sensitive data require Sec-Fetch-Mode: navigate; fetch() of another app's HTML is blocked.
  • App-name validation — app_table access is checked with a dot-boundary comparison; com.example.my cannot read com.example.myapp.posts.
  • Grantee validation — for any cross-user access the requesting user must appear in the record's permission grantees list.
  • Token type — tokens are explicitly typed 'browser' (session-bound) or 'oauth' (cookieless, for programmatic access). Browser tokens are invalidated when the session ends.

Content Security Policy

App pages are served with a strict CSP. The default header is:

default-src 'self';
script-src  'self' 'nonce-<per-request>';
style-src   'self' 'unsafe-inline';
img-src     * data:;
connect-src 'self';
form-action 'self';
frame-src   'none';
object-src  'none';

The nonce is crypto.randomBytes(16) generated per request — only scripts explicitly listed in the app manifest and injected by the renderer receive it. Inline <script> tags, onclick="..." handlers, and HTML-injected scripts are blocked even if the page is otherwise compromised.

Apps may request CSP relaxations via manifest permissions. These are user-grantable at install time and currently include:

PermissionEffect
allow_self_framesframe-src 'self' blob: — same-origin iframes / blob previews.
external_fetchAdds whitelisted domains to connect-src.
external_scriptsAdds whitelisted domains to script-src; SRI required.
unsafe_evalAdds 'unsafe-eval' to script-src.
Known limitation: img-src *. Image URLs can reach any host, which means an app's own code can exfiltrate data it already has via encoded image requests. This is an accepted trade-off — running third-party JS inherently involves trust, and the user controls which apps are installed. Restricting img-src is a future option for higher-security deployments.

Tokens & sessions

CredentialScopeLifetimeStorage
session cookieGlobal (/)30 dayshttpOnly, SameSite=Strict, server-side session file
browser app tokenPath-scoped to /apps/<app>Issued per page load, session-boundSecure, readable from JS (required)
oauth app tokenApp-bound, cookieless6 monthsReturned to client once, used as Authorization header

Server-side protections

  • Rate limiting — 300 requests/minute per authenticated user across CEPS + FEPS routes, with graduated throttling and a 429 Retry-After response when exceeded.
  • Per-record size limit — 2 MB max per record on create / update / replace.
  • Regex safety$regex values validated to reject nested-quantifier catastrophic patterns; alternatively, operator can be disabled per deployment.
  • Operator blocking$where, $function, $accumulator, $expr stripped from user queries before they reach MongoDB.
  • Input validation — permission names, function names, and table ids are all restricted to [a-zA-Z0-9._-]+. Zip extraction enforces zip-slip protection.
  • Public routes — path-isolated from the app cookie namespace. preventCookiesOnPublicRoutes middleware ensures public pages never see auth credentials.
  • Reserved prefixespublicids starting with /app, /admin, /account etc. are blocked, even for admins.
install / file structure

File Structure

A quick tour of what lives where in the freezr checkout.

Folders

The top-level folders in the freezr repo:

FolderWhat's in it
server.mjsThe entry point. Boots Express, wires middleware, loads the feature routers, and starts listening.
features/The actual product, split by domain: account/, admin/, apps/, creator/, oauth/, public/, register/. Each feature has its own routes, controllers, and services.
froutes/Top-level route composition — the index that mounts every feature's router onto the Express app.
middleware/Cross-cutting middleware: auth/ (session, API tokens, OAuth), permissions/, tokens/, plus the request logger, session store, and dev assertions.
adapters/Pluggable back-ends: datastore/ (NeDB, MongoDB), http/ (file-system connectors for local, S3, Azure, Dropbox, GoogleDrive), llmConnectors/ (Claude, ChatGPT), and rendering/.
common/Shared helpers that don't belong to a single feature: helpers/, startup/ sequencing, loganalytics/, and small utilities under misc/.
freezrsystmapps/The built-in system apps that ship with every freezr server: info.freezr.account, info.freezr.admin, info.freezr.creator, info.freezr.public, info.freezr.register.
users_freezr/Runtime data for the local FS adapter — every user gets a folder here (see below). When you use S3/Azure/Dropbox this folder would be on your cloud storage.
users_3Pfunctions/Scratch space for locally-registered third-party microservices (the "Add a Local Microservice" admin feature).
users_temp_logs/Per-request log files produced by the request logger. Rotated; safe to delete.
test/Mocha test suites: unit/, integration/ (ceps, feps, oauth, coreApiV1/V2), and e2e/.
node_modules/Installed dependencies (see the next sub-section).

Inside users_freezr/, each user (including the admin) has a folder with the same three sub-folders:

PathWhat's in it
users_freezr/<user>/files/Per-app data directories, one per app the user has installed (e.g. cards.hiper.freezr/, com.salmanff.notery/). Plus @ and @public sharing directories and app_files/ for pages/assets served up to other apps.
users_freezr/<user>/apps/The installed code for each app (HTML, CSS, JS, manifest). Separate from files/ so that data survives an app upgrade. Admin accounts also keep zipped backups here.
users_freezr/<user>/db/NeDB on-disk databases when the user's DB adapter is nedb. With Mongo this is empty.

A number userIds are reservede - eg fradmin (the built-in "freezr admin" system user) and public/ where pulished items live.

Node modules

freezr tries to keep its dependency list small. The sections below split the runtime modules into the Express core, general-purpose utilities, and the back-end adapter SDKs.

Core

The Express stack every request passes through.

ModuleWhy it's here
expressThe HTTP server and routing framework.
express-sessionCookie-based sessions for logged-in users.
cookie-parserParses cookies on incoming requests (used by the auth middleware).
multerMultipart form parsing for file uploads (profile pictures, app zips, user content).

Utilities

Cross-cutting helpers used throughout the server.

ModuleWhy it's here
bcryptjsPassword hashing — user passwords are never stored in plain text.
node-cacheIn-memory cache for hot records, preferences, and permission lookups.
node-fetchHTTP client for outbound calls (inter-freezr federation, some adapters, LLM streaming).
fflatezip/unzip utility — used when installing apps from .zip bundles and when exporting user data.
sharpImage processing: resizing profile pictures and rasterising LLM-generated SVGs to PNG.
asyncFlow-control helpers (waterfalls, parallel limits). Currently used by some legacy file-system adapters and will be removed in the future.
(redis)This is included here as an aspirational entry - hopefully it will be added to the package.json in the future.

Adapters

SDKs for the pluggable databases, file systems, and LLM providers. Each is only loaded when the corresponding adapter is active.

ModuleWhy it's here
nedb-asyncfsThe embedded, file-backed database used by the nedb datastore adapter — zero-config default for laptops and small servers.
mongodbOfficial MongoDB driver, used by the mongodb and cosmosformongostring adapters.
@aws-sdk/client-s3S3 reads/writes for the aws file-system adapter.
@aws-sdk/client-iamUsed when S3 bootstrap needs to create / check buckets and policies.
@aws-sdk/client-lambdaInvokes user-supplied Lambda functions for the serverless compute adapter.
@azure/storage-blobBlob reads/writes for the azure file-system adapter.
@azure/identityAzure credential discovery (managed identity, service principals) used by the Azure adapter.
dropboxDropbox SDK for the dropbox file-system adapter (OAuth + file API).
googleapisGoogle SDK for the googleDrive file-system adapter.
@anthropic-ai/sdkAnthropic client for the Claude LLM connector.
openaiOpenAI client for the ChatGPT / gpt-image LLM connector.

Dev-only (devDependencies) adds mocha + chai for tests, standard for linting, and the @codemirror/* and rollup packages that power the in-browser code editor bundles used by the built-in creator app.

You can learn more about how to connect to these adapters on the install page.

architecture / http api

HTTP endpoints

To use freezr, the API provides all you need. The freezr API itself is a wrapper over two HTTP surfaces. CEPS (Common Endpoints) is the portable layer any CEPS-compatible server speaks. FEPS (freezr Extensions) is the freezr-specific layer: bulk operations, uploads, permissions, messaging, LLM, and serverless.

These endpoints are listed here for reference, but they should not normally be used by apps. Apps should access these via the freezr API.

All endpoints require a Authorization: Bearer <app_token> header. The token is read from the path-scoped app_token_<user_id> cookie issued at page load. A few endpoints (ping, login, public query, self-register) are explicitly unauthenticated.

CEPS — data

POST /ceps/write/<app_table> Create a record
URL
app_table string required
Full <app_id>.<table>.
Body (JSON)
The record contents. _id assigned by server.
// request
POST /ceps/write/com.you.notes.entries
{
  "title": "Hello",
  "body":  "First note"
}
// response
{
  "_id": "abc123"
}
GET /ceps/read/<app_table>/<id> Read a record by id
Query params (optional)
owner_id string
Read from another user's store.
requestor_app string
Calling app identifier (auto-filled by client).
permission_name string
// request
GET /ceps/read/com.you.notes.entries/abc123
// response
{
  "_id": "abc123",
  "title": "Hello",
  "body":  "First note",
  "_date_modified": 1740000000000
}
POST /ceps/query/<app_table> Query with a MongoDB-style filter
Body (JSON)
q object required
Filter. $where, $function, $accumulator, $expr are rejected.
sort, count, skip object | number
// request
POST /ceps/query/com.you.notes.entries
{
  "q":    { "archived": false },
  "sort": { "_date_modified": -1 },
  "count": 20
}
// response
[
  { "_id": "abc123", "title": "Hello", ... }
]
PUT /ceps/update/<app_table>/<id> Replace a record (all fields)
Body (JSON)
New record contents. In CEPS, replacement is always full.
// request
PUT /ceps/update/com.you.notes.entries/abc123
{ "title": "Updated", "body": "New" }
DEL /ceps/delete/<app_table>/<id> Delete a record by id
Note
For bulk deletion by query, use the FEPS variant below.
// request
DELETE /ceps/delete/com.you.notes.entries/abc123

CEPS — permissions & messaging

EndpointPurpose
GET /ceps/perms/getList permissions granted to the calling app
POST /ceps/perms/share_recordsGrant or revoke access to records for specific grantees (or _public)
POST /ceps/perms/validationtoken/setIssue a cross-server validation token (data-owner federation)
GET /ceps/perms/validationtoken/validateValidate a federated data-owner token
POST /ceps/message/initiateSend a record-linked message
POST /ceps/message/mark_readMark messages as read (by id or all)
GET /ceps/messagesList messages for the calling app
GET /ceps/pingHealth check; returns server_type and version
POST /public/query / /public/query/@<owner>Query public records (no auth)

FEPS — freezr extensions

FEPS endpoints cover operations beyond the portable baseline: upserts with forced ids, field-level updates, bulk delete, file upload, LLM, and serverless invocation. They also carry permission context in the body (permission_name, owner_id, requestee_app) for cross-app access.

EndpointPurpose
POST /feps/write/<app_table>[/<id>]Create with body { _entity, data_object_id?, upsert?, permission_name?, owner_id?, requestee_app? }
PUT /feps/update/<app_table>[/<id>]Partial update, or bulk update by q. Pass ?replaceAllFields=true for full replacement.
DELETE /feps/delete/<app_table>[/<id>]Delete by id or by query q.
PUT /feps/upload/<app>Multipart file upload. Form fields: file, options (JSON).
GET /feps/userfiles/<app>/<user>/<file_id>Stream a stored file. Content-Type derived from extension.
GET /feps/manifest[/<app_name>]Fetch an installed app's manifest JSON.
PUT /feps/llm/askStreaming LLM completion (SSE: delta, thinking, done, error events).
PUT /feps/llm/generate_imageImage generation (returns base64 PNG or SVG).
PUT /feps/serverless/<task>Serverless dispatch. task is one of invokeserverless, invokelocalservice, createinvokeserverless, upsertserverless, updateserverless, deleteserverless, rolecreateserverless, deleterole, upsertlocalservice, deletelocalfunction.
GET /feps/serverless/getalllocalfunctionsList installed local functions.

Account API

EndpointPurpose
GET /acctapi/getAppListList installed apps
GET /acctapi/getUserPrefsUser preferences (theme, defaults, etc.)
GET /acctapi/getAppResourceUsagePer-app storage / request usage
PUT /acctapi/app_install_from_zipfileUpload a packaged app zip; filename must match the app identifier
POST /acctapi/loginSession login