Skip to content

Deploy on your server

This content is not available in your language yet.

You run your agents on a server of your own. Geometer charges for assurance (Guard holding the limits outside the AI, the signed licence, alerts from outside your server, the monthly report with its QR check, crash tests), not for hosting. The server, its bill, your AI key and your WhatsApp account are yours.

  1. Make an Ubuntu server. Ubuntu 22.04 or 24.04, 2 vCPU and 4 GB for about ten shops, with ports 22, 80 and 443 open. Pick your host: DigitalOcean (Bangalore), AWS Lightsail (Mumbai), Google Cloud Compute Engine (Mumbai), Hetzner, or any Ubuntu server.

  2. Point your domain at it. An A record to the server’s address. HTTPS gets its certificate by itself.

  3. Get a pairing code. On geometer.dev/account choose Add a server. The code works once and runs out after two hours.

  4. Run the install, one of two ways:

    • On a host with a user data field (DigitalOcean, Hetzner and most others), paste bundle/cloud-init.yaml with its four FILL IN values filled (your SSH key, the release tag, your domain, the pairing code). The server installs itself and makes a user builder.

    • Anywhere else, log in as a normal user with sudo and run:

      Terminal window
      git clone --branch <release tag> https://github.com/GeometerLabs/agents.git ~/geometer/agents
      bash ~/geometer/agents/install.sh --pairing-code-file <(printf '%s\n' '<your pairing code>')

      The account page shows these two lines with your code already in them. It asks for your domain, says what it will do, and asks before it changes anything.

  5. Sign in. Run geometer invite builder on the server for a one-time link to the panel. Its first run asks for the model and its key, the backup bucket and its key, and Google if your shops use a calendar. Then add a shop.

What the install does: it installs Docker Engine and Node if they are missing (each checked against its publisher’s key or checksum), checks this copy is the pinned release, loads the Guard image, pairs the server with Geometer (the code is sent once), makes the server folder, starts the deployer as a system service, and starts HTTPS and the panel. Run it again at any time: it only repairs or carries on. --dry-run prints each step and changes nothing.

After the install, geometer status, geometer logs, geometer invite builder, geometer stop <shop> and geometer start <shop> work on the server. The sections after the appendix heading (backups, a model you host, every day) apply either way.

Each shop gets its own runtime and its own Guards (Razorpay, WhatsApp, and Google Calendar for Bookings). The server adds one model Guard, one inbox for webhooks, one relay for owner approvals, HTTPS, a nightly snapshot of every shop’s data, and a heartbeat to Geometer.

Guard sits in front of every call the agent makes. The agent holds no key. The only things it can reach are its own Guards and the model Guard. If a Guard is down, nothing goes out.

Before anything starts, the deployer asks each Guard to refuse a forbidden call made from each rule. A deploy that cannot show this is refused, names the rule, and starts nothing.

  • An Ubuntu 22.04 or 24.04 server, 2 vCPU and 4 GB for about ten shops. The install adds Docker Engine and Node 26 if they are missing.
  • A domain with an A record pointing at the server. HTTPS (Caddy) gets its certificate by itself.
  • Per shop: its Razorpay key and webhook secret, its WhatsApp token and app secret, its Google service account if it has a calendar, and its settings.
  • One API key for the model every shop on the server may use.
  • One place that is not this server, for the nightly snapshots: any S3-compatible bucket (Google Cloud Storage, Backblaze B2, Cloudflare, MinIO). Its key must be able to write, and to read for the weekly check and for a restore. Set a lifecycle rule on the bucket that deletes snapshots after 30 days or more. The bundle deletes old snapshots itself only when its key is allowed to.
  • A pairing code from your account page (or a deployment key from Geometer, gfk_...). Without pairing the server runs, but nobody outside hears if it dies, and no shop starts with a production Guard image.

Open ports 22, 80 and 443.

The same thing step by step, for a server you set up yourself or when you want to see each part. In the commands below, run node bundle/geometer.ts from the agents folder. Set GEOMETER_SERVER to your server folder, or add --server <folder>. You need Node 26 and Docker Engine (not Docker Desktop) installed yourself on this path.

One example: from an empty server to the first shop

Section titled “One example: from an empty server to the first shop”
Terminal window
git clone https://github.com/GeometerLabs/agents.git && cd agents
docker load < ~/geo-guard-0.1.0-alpha.2-pathways.1-x86_64.tar
node bundle/geometer.ts init ~/geometer-server \
--domain shops.example.com --reviewed-by your-name \
--model-wire openai --model gpt-4o-mini \
--fleet-url https://fleet.geometer.dev --fleet-key-file ~/geometer-server/fleet_key \
--snapshots-url s3://my-bucket/snapshots --snapshots-endpoint https://storage.googleapis.com \
--age-identity-out ~/age-identity.txt
export GEOMETER_SERVER=~/geometer-server
node bundle/geometer.ts model-key ~/model-key.txt
node bundle/geometer.ts backup-key snapshots ~/snapshots-key.txt
node bundle/geometer.ts shop add glow-salon --template bookings --whatsapp-number-id 123456789012345 --prepared-by "Divya, local partner"
node bundle/geometer.ts check glow-salon
node bundle/geometer.ts deploy glow-salon

What each step does:

  1. init makes the server folder and a key pair for the snapshots. The secret half goes to ~/age-identity.txt. Move it off the server (a password manager, your laptop). It’s the only key that opens a snapshot, so it must not sit beside the snapshots: you bring it for a restore, and take it away again.
  2. model-key and backup-key hand in the server’s keys.
  3. shop add makes the shop from a template. --prepared-by is the name the owner reads in the monthly report (“Set up by: Divya, local partner”). Without it the message says “your builder”.
  4. Hand in the shop’s keys as files in secrets-inbox/glow-salon/, one file each: razorpay_key (key_id:key_secret), whatsapp_token, razorpay_webhook_secret, whatsapp_app_secret, whatsapp_verify_token, and google_service_account.json for a calendar. The deployer moves each into the one container that needs it and deletes it from the inbox. The panel does this for you when you set a shop up there.
  5. check starts nothing and lists everything wrong at once.
  6. deploy starts the shop and prints what the running Guards confirmed, for example 12 of 12 limits proven to bite. The go-live certificate quotes that line.

If a rule cannot be shown to bite, you see this and nothing starts:

This was refused, and nothing was started or changed:
- [tripwire:deposit-amount] g-glow-salon-razorpay: the rule "deposit-amount" would let a forbidden call through (0 of 4 tripwires passed). Nothing was started.

Point each provider at the shop’s address, shown in the panel and in status/glow-salon.json:

  • Razorpay’s webhook: https://shops.example.com/c/glow-salon/webhooks/razorpay
  • WhatsApp’s callback: https://shops.example.com/c/glow-salon/webhooks/whatsapp, with the verify token you handed in.

The inbox checks each provider’s signature before it passes anything on. A provider that does not sign its webhooks gets a secret path instead, which is weaker.

The deployer applies what the panel writes, renews the relay’s credential every day, takes the nightly snapshot and runs the weekly restore drill. Run it as a service:

ExecStart=/usr/local/bin/node bundle/geometer.ts apply --watch
Restart=always

The model Guard calls a hosted provider with the key you hand in. You can point it at a model you run yourself instead: any server that speaks OpenAI-compatible chat completions, such as vLLM, llama.cpp’s server or Ollama. Every call still goes through the model Guard, with the same model pin, daily cap and kill switch. If you do nothing, nothing changes. Geometer’s partner server does not take this setting.

"model": {
"wire": "openai",
"model": "llama-local",
"selfHosted": {
"url": "https://models.example.in/v1",
"rate": { "inputUsdPerMillion": 0.05, "outputUsdPerMillion": 0.10 }
}
}
  1. Start your model server with a key, and note the model name it answers to.
  2. Add selfHosted to the model block of geometer.json, as above. Use https. Plain http is taken only for a private address on your own network (10.x, 172.16 to 31.x, 192.168.x).
  3. Hand in the model server’s key with model-key, as for a hosted provider. It goes to the model Guard only.
  4. rate is optional: your own price per million tokens, in US dollars. The running costs count each call at it. Without it the calls are counted with no price, and the model line says it is a floor.
  5. Run check for a shop, then deploy with no shop name. A change to geometer.json does not redeploy shops by itself.

geo-model-gateway, which the installer puts next to geo, can stand in front of your model server. It adds tokens, a monthly limit per account and a usage report, and keeps no message text. Its address is then the url, and a token it issues is the key you hand in. A config for a model server on the same machine:

listen = "0.0.0.0:8787"
store = "/var/lib/geo-model-gateway/gateway.db"
[upstream]
kind = "openai"
base_url = "http://127.0.0.1:8000/v1" # your model server
api_key_env = "MODEL_SERVER_KEY" # the variable that holds its key
[[model]]
id = "llama-local" # the name in geometer.json
prompt_usd_per_million = 0.05 # your own prices: the same as "rate"
completion_usd_per_million = 0.10

Then geo-model-gateway --config gateway.toml account create my-server prints an account id, token issue <account id> prints the token once, and serve starts it. usage report shows the month’s calls and spend.

How your shops’ data is kept

What How often Where
Snapshot of every runtime database and each Guard’s own store Every night, kept 30 days Your bucket: compressed, then encrypted with your age key
Weekly check, on the server Every week Reads each file of the newest snapshot back and compares it with what was uploaded, and checks its record against the live one. It doesn’t need your key
Full restore test, by you Once a month On your own machine, with your key: node bundle/backup/restore-snapshot.ts <snapshot folder> --identity <key file> --out <empty folder> opens one and checks every file

A snapshot that did not happen, or a check that failed, shows in status and alerts from outside the server. To run them once by hand:

Terminal window
node bundle/geometer.ts backup now
node bundle/geometer.ts backup drill

There’s no continuous copy: a shop’s data in a snapshot is up to a day old. Every decision is also in the shop’s signed record, and Razorpay, WhatsApp and the calendar keep their own records, so a restored shop looks those up before it sends anything again.

The bucket’s lifecycle rule deletes snapshots older than 30 days. A key that may only write is fine: then the rule does all the deleting.

Terminal window
node bundle/geometer.ts status
node bundle/geometer.ts logs glow-salon
node bundle/geometer.ts stop glow-salon --reason "owner asked"
node bundle/geometer.ts start glow-salon
node bundle/geometer.ts restore glow-salon --identity ~/age-identity.txt --at 2026-11-04T10:30:00Z
  • status shows each shop: running or not, its Guards, the last backup and drill.
  • stop is the Guard kill switch: nothing new goes out. The record says who stopped it. The panel can stop a shop, but starting it again is done here with start.
  • restore puts the shop’s data back from the newest nightly snapshot at or before that time (the newest of all without --at). It checks the snapshot first, then stops only the shop’s runtime, puts the data in place and starts it. What came in after the snapshot isn’t in the shop’s record, so the shop looks up each payment link, refund and calendar entry with the provider before it sends anything.
  • restore needs your key: bring the key file to the server for the restore, then take it away again.
  • Backups and the restore drill cover shops. The Guards of an agent you wrote are not in them yet.
  • A model you host, and geo-model-gateway in front of it, have been tried with Geometer’s tests and a stand-in model server, not yet with vLLM, llama.cpp or Ollama themselves.
  • The deploy has been tested by Geometer’s own tests. No real business has run on it yet.