Deploy the console

Run the console and API on your own infrastructure with PostgreSQL, then create the first administrator.

On this page

The console is a TanStack Start server that serves the administrator interface and the /api/v1 endpoints enrolled browsers call. PostgreSQL stores organizations, policies with every version, teams, devices and request evidence. You run both.

During the pilot, shim shares a release of the source with your team. The commands in these docs run from the root of that checkout, and the console and the extension you deploy must come from the same release.

Requirements

  • Bun 1.4.2
  • PostgreSQL 17
  • An HTTPS origin for the console, such as https://security.example.com
  • Docker, if you build the container image

Configure the environment

The server, the migration command and the bootstrap command all read these variables at runtime. Keep them out of images and source control and inject them when each command starts.

Variable Value
DATABASE_URL A postgres:// or postgresql:// connection URL
BETTER_AUTH_SECRET A random secret of at least 32 characters
BETTER_AUTH_URL The console origin. Must be HTTPS, except http://localhost or http://127.0.0.1 for local evaluation
PORT Optional. The port the server listens on. Defaults to 3000
HOST Optional. The address the server listens on. The container image sets 0.0.0.0

BETTER_AUTH_URL must match the address people type into the browser exactly. Sign-in cookies and origin checks depend on it, and it is the platform URL you give to the extension.

Security. Use TLS for the database connection, terminate HTTPS in front of the server, and configure trusted proxy and client IP handling at that proxy.

Build

From the root of your checkout, install dependencies and build the console:

bun install --frozen-lockfile
bun run --cwd apps/platform build

To build the container image instead, run this from the root of your checkout, because the image needs the whole workspace:

docker build -f apps/platform/Dockerfile -t shim-console .

The image contains only the built server. It runs as the bun user and listens on port 3000 on all interfaces. Start it with the three required variables passed from your environment, and publish the port only to the proxy that terminates HTTPS:

docker run -d --name shim-console -p 127.0.0.1:3000:3000 \
  -e DATABASE_URL -e BETTER_AUTH_SECRET -e BETTER_AUTH_URL \
  shim-console

Apply migrations

Migrations and bootstrap are not in the container image. Run them from a checkout of the same release, after bun install --frozen-lockfile, with the three required variables set and DATABASE_URL pointing at the production database.

Run migrations once for each release, before the new server starts. The command is safe to repeat: it takes a database lock and skips migrations that are already applied.

bun run db:migrate

Create the first administrator

Sign-up is disabled. Bootstrap creates one organization, its first administrator and the default policy. Run it once, from the same checkout and with the same variables as the migrations.

Generate the password in your password manager. Run this line on its own, paste the password and press Enter. Nothing is shown, and the password stays out of shell history:

read -rs ADMIN_PASSWORD && export ADMIN_PASSWORD

Then run bootstrap and clear the variable:

ADMIN_EMAIL=admin@example.com \
ADMIN_NAME='Security team' \
ORGANIZATION_NAME='Your organization' \
bun run db:bootstrap
unset ADMIN_PASSWORD
Variable Rule
ADMIN_EMAIL A valid email address
ADMIN_PASSWORD 12 to 128 characters
ADMIN_NAME Optional, up to 80 characters. Defaults to Administrator
ORGANIZATION_NAME 1 to 120 characters

Security. Do not type credentials inline in a command, especially in shared shells: they persist in shell history and can appear in process lists.

Limit. The console has no invitations, single sign-on, multi-factor authentication or account management yet. Bootstrap is the only way to create an administrator.

Start the server

Without the container, start the built server from apps/platform with the environment injected:

bun .output/server/index.mjs

bun run start does the same and also loads a local apps/platform/.env file. Open the console origin and sign in with the bootstrap credentials.

While the server runs, an hourly task removes prompt content older than 90 days. Set your database backup retention so deleted prompts do not outlive that window in backups.

Logs and telemetry

When a request fails unexpectedly, the server writes one fixed line, Platform request failed or Authentication request failed, with no request body, headers, prompt text, credentials or error detail. The client receives only an error code, such as unsupported_client_version.

Device credentials and one-time enrollment codes are stored as SHA-256 hashes, and administrator passwords are stored hashed, so the database holds none of them in readable form.

On every sync, each enrolled browser sends a heartbeat with four fields: its extension version, the policy version it holds, its composer status and the AI apps it had open since its last successful heartbeat, each as an app ID and a day. The list is empty when the browser’s policy turns AI app discovery off, and the console stores app days only from browsers whose policy turns it on. Reports of matched requests go only to your console origin. The device credential stays in the extension’s background and is never given to ChatGPT, Claude or Gemini pages.

Logs written by your hosting platform, proxy and database are yours to configure. Keep request bodies out of them: report uploads to /api/v1/events carry captured prompt text.

Evaluate on one machine

For a local evaluation with Docker running, the checkout creates its own configuration and database:

bun install --frozen-lockfile
bun run setup
bun run db:up
bun run db:migrate
bun run db:bootstrap
bun run dev

bun run setup writes a root .env with the local database password, and apps/platform/.env with the console secrets and a generated administrator password. It refuses to overwrite existing configuration. bun run db:up starts PostgreSQL 17 on 127.0.0.1:54329. The console opens at http://localhost:3000; sign in as admin@example.test with the ADMIN_PASSWORD from apps/platform/.env.

Security. Local credentials and test prompts must never reach production services.