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.
The one-step path
Section titled “The one-step path”-
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.
-
Point your domain at it. An
Arecord to the server’s address. HTTPS gets its certificate by itself. -
Get a pairing code. On geometer.dev/account choose Add a server. The code works once and runs out after two hours.
-
Run the install, one of two ways:
-
On a host with a user data field (DigitalOcean, Hetzner and most others), paste
bundle/cloud-init.yamlwith its fourFILL INvalues filled (your SSH key, the release tag, your domain, the pairing code). The server installs itself and makes a userbuilder. -
Anywhere else, log in as a normal user with
sudoand run:Terminal window git clone --branch <release tag> https://github.com/GeometerLabs/agents.git ~/geometer/agentsbash ~/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.
-
-
Sign in. Run
geometer invite builderon 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.
What you get
Section titled “What you get”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.
What you need
Section titled “What you need”- 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
Arecord 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.
Appendix: by hand
Section titled “Appendix: by hand”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”git clone https://github.com/GeometerLabs/agents.git && cd agentsdocker 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-servernode bundle/geometer.ts model-key ~/model-key.txtnode 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-salonnode bundle/geometer.ts deploy glow-salonWhat each step does:
initmakes 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.model-keyandbackup-keyhand in the server’s keys.shop addmakes the shop from a template.--prepared-byis the name the owner reads in the monthly report (“Set up by: Divya, local partner”). Without it the message says “your builder”.- 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, andgoogle_service_account.jsonfor 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. checkstarts nothing and lists everything wrong at once.deploystarts the shop and prints what the running Guards confirmed, for example12 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.The HTTPS entry
Section titled “The HTTPS entry”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.
Keep the deployer running
Section titled “Keep the deployer running”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 --watchRestart=alwaysA model you host (optional)
Section titled “A model you host (optional)”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 } }}- Start your model server with a key, and note the model name it answers to.
- Add
selfHostedto themodelblock ofgeometer.json, as above. Usehttps. Plainhttpis taken only for a private address on your own network (10.x, 172.16 to 31.x, 192.168.x). - Hand in the model server’s key with
model-key, as for a hosted provider. It goes to the model Guard only. rateis 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.- Run
checkfor a shop, thendeploywith no shop name. A change togeometer.jsondoes 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 serverapi_key_env = "MODEL_SERVER_KEY" # the variable that holds its key
[[model]]id = "llama-local" # the name in geometer.jsonprompt_usd_per_million = 0.05 # your own prices: the same as "rate"completion_usd_per_million = 0.10Then 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.
Backups
Section titled “Backups”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:
node bundle/geometer.ts backup nownode bundle/geometer.ts backup drillThere’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.
Every day
Section titled “Every day”node bundle/geometer.ts statusnode bundle/geometer.ts logs glow-salonnode bundle/geometer.ts stop glow-salon --reason "owner asked"node bundle/geometer.ts start glow-salonnode bundle/geometer.ts restore glow-salon --identity ~/age-identity.txt --at 2026-11-04T10:30:00Zstatusshows each shop: running or not, its Guards, the last backup and drill.stopis 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 withstart.restoreputs 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.restoreneeds your key: bring the key file to the server for the restore, then take it away again.
Not built yet
Section titled “Not built yet”- 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-gatewayin 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.