Saltar al contenido
miblo

Servidor propio

El servidor de Miblo Remote, para levantar en tu propio host. Con él, el móvil y la computadora hablan solo a través de tu servidor, nunca de miblo.ai. Es opcional y gratis, para quien sabe alojarlo.

Descargar

Versión 1.3.0

Se distribuye solo desde miblo.ai y está firmado con la misma clave de las actualizaciones de Miblo (Ed25519). Verifica la firma antes de usarlo: el comando está justo debajo, en Descargar y verificar.

Licencia AGPL-3.0, dentro del paquete.

¿Prefieres Git? El código fuente también está en GitHub: github.com/marcus-campos/miblo-relay

Guía del servidor

El README del paquete de esta versión.

The phone server of Miblo, to run on your own host: the session relay, the account your phones join, the phone registry and the phone app (PWA). It speaks the very protocol the Miblo plugin and miblo.ai speak, so everything of Miblo+ that goes through the phone (history, replies, approvals, remote tasks) works here, free and without a license, for anyone who runs their own server.

Privacy

Miblo is already end-to-end encrypted: the keys are made on your computer, reach each phone sealed to that phone's own key only after you type, on the computer, the 6-digit code the phone shows, and the server only relays ciphertext. A malicious server (miblo.ai or this one) reads nothing, cannot slip a phone of its own in, and cannot act on your computer.

What remains is the phone app's code: it is a web page, and whoever serves the page runs code with the phone's keys. On miblo.ai you trust what miblo.ai serves. On your own server, the app is what you built from this source (or a release whose hashes you checked), served by you. That removes the trust in the JavaScript miblo.ai serves. Details in the threat model (docs/threat-model.md).

While your server is set, the plugin sends nothing of the phone companion or the account to miblo.ai (it still reads its signed update manifest from miblo.ai).

Download and verify

The download comes from miblo.ai, at https://miblo.ai/dl/relay/, next to the firmware and the installers (the source is also on GitHub: https://github.com/marcus-campos/miblo-relay), next to the firmware and the installers, and signed with the key of Miblo's releases (Ed25519). The newest version is in https://miblo.ai/dl/extras/latest.json; the page miblo.ai/en/docs/self-hosting shows this guide and links it.

V=1.0.0   # the newest version: https://miblo.ai/dl/extras/latest.json
curl -fLO https://miblo.ai/dl/relay/miblo-relay-$V.tar.gz
curl -fLO https://miblo.ai/dl/relay/miblo-relay-$V.tar.gz.sig
node -e 'const c=require("crypto"),fs=require("fs"),f=process.argv[1],k=c.createPublicKey({key:{kty:"OKP",crv:"Ed25519",x:Buffer.from("LGtuUvDA506N8iD1tolbQzWAJBb9hUerW0OH4C2pBek=","base64").toString("base64url")},format:"jwk"});const ok=c.verify(null,fs.readFileSync(f),k,Buffer.from(fs.readFileSync(f+".sig","utf8").trim(),"base64"));console.log(ok?"signature OK":"BAD SIGNATURE: do not use this file");process.exit(ok?0:1)' miblo-relay-$V.tar.gz
tar -xzf miblo-relay-$V.tar.gz && cd miblo-relay-$V

The public key (LGtuUvDA506N8iD1tolbQzWAJBb9hUerW0OH4C2pBek=) is the one the Miblo plugin checks its updates with. https://miblo.ai/dl/relay/SHA256SUMS (signed in SHA256SUMS.sig) holds the SHA-256 of every published version.

Quick start (Docker, 3 steps)

You need a machine with Docker, a DNS name pointing at it and ports 80 and 443 open (Caddy gets the TLS certificate by itself).

1. Configure it, inside the folder you downloaded and checked above:

cd deploy/docker
cp .env.example .env   # set RELAY_DOMAIN to your domain

2. Start it

docker compose up -d --build
docker compose logs relay   # shows the setup token and the server's identity fingerprint

3. Create your account: open https://your-domain/conta (or /en/account), use the setup token, set a password and add a second factor (a passkey, recommended, or an authenticator app). Generate the recovery codes.

Without Docker: npm ci && npm run build && PUBLIC_ORIGIN=https://your-domain node dist/server.mjs (Node 22.13+ or 23.4+, which have node:sqlite) behind any TLS proxy.

Deploy to your own Cloudflare

A Worker on your Cloudflare account (the free plan is enough for one person), with D1 and Durable Objects:

npm ci
cp deploy/cloudflare/wrangler.jsonc.example deploy/cloudflare/wrangler.jsonc
cd deploy/cloudflare
npx wrangler d1 create miblo-relay          # put the database_id in wrangler.jsonc
npx wrangler d1 migrations apply miblo-relay --remote
cd ../.. && npm run build:app && npm run cf:secrets   # prints the setup token and the fingerprint
cd deploy/cloudflare && npx wrangler deploy

Then step 3 above.

Point your computer at it

In your own terminal (never through an AI agent), with a Miblo plugin that has miblo server:

miblo server set https://your-domain

Miblo reads the server's identity key, shows its fingerprint (compare it with the one your server printed: docker compose logs relay or node dist/server.mjs fingerprint) and asks for the code on your Miblo's screen. In the Miblo desktop app: Settings › Phone server. Switching unlinks this computer and turns the phone companion off at the old server; then:

miblo account link   # confirm the code at https://your-domain/plus/link
miblo phone on

On the phone, open https://your-domain/app, sign in and allow the phone on the computer with the code it shows. miblo server shows where it points; miblo server reset goes back to miblo.ai (also with the Miblo's code).

Configuration

VariableRequiredWhat it is
PUBLIC_ORIGINyesThe server's https address, the origin alone (https://relay.example.com): the app's origin, the passkeys' relying party, the device-link address. Never taken from a request.
TRUSTED_PROXYbehind a proxyYour proxy's addresses or ranges (CIDR), comma-separated (172.30.247.0/24). Only for a connection from them is the client address the last X-Forwarded-For entry (the one the proxy added). Only a rate-limit key. Never set it without a proxy in front.
RELAY_VAPID_SUBJECTnoA contact sent to the push services (mailto:you@example.com); defaults to the server's address.
SESSION_SECRET, MFA_KEY, SERVER_IDENTITY_KEY, RELAY_VAPID_PUBLIC_KEY, RELAY_VAPID_PRIVATE_KEY, SETUP_TOKENno (Node) / yes (Cloudflare)On Node, the ones you do not set are made once and kept in secrets.json (0600), in $MIBLO_RELAY_SECRETS_DIR. On Cloudflare, npm run cf:secrets makes and stores them all.
MIBLO_RELAY_DATAnoThe database folder (Docker: /data).
MIBLO_RELAY_SECRETS_DIRnoWhere secrets.json lives (default: MIBLO_RELAY_DATA; Docker: /secrets, a volume of its own, kept out of the database's backups).
PORT, HOSTnoWhere Node listens (default 0.0.0.0:8787).

Node commands: node dist/server.mjs check, fingerprint, setup-token (a setup token works for one setup; once used, this command makes a new one, to redo an abandoned setup), reset-account (lost every factor: removes the factors and sessions and makes a new setup token; computers and phones stay). On Cloudflare, delete the mfa_* and sessions rows with wrangler d1 execute and set a new token with npx wrangler secret put SETUP_TOKEN (a long random value, like openssl rand -base64 24) for the same.

Upgrading

Download and verify the new version as in Download and verify, copy your deploy/docker/.env into it (on Cloudflare, your deploy/cloudflare/wrangler.jsonc) and, in the new folder:

cd deploy/docker && docker compose up -d --build

The Docker volumes (database and secrets) stay the same: the Compose project is named after its folder (docker), the same in both versions. Database migrations run at start (Node). On Cloudflare: npm ci && npx wrangler d1 migrations apply miblo-relay --remote && npm run build:app && npx wrangler deploy. Watch the versions in https://miblo.ai/dl/extras/latest.json (security fixes are announced on miblo.ai/en/docs/self-hosting) and back up the relay-data volume first.

Security

  • Read the threat model (docs/threat-model.md): a malicious operator is as powerful as a malicious miblo.ai, which the protocol already tolerates.

  • One account, second factor mandatory; the password alone never signs in. Passwords use PBKDF2-SHA-256 (600,000 iterations on Node; 100,000 on Cloudflare, a Worker's maximum). Wrong passwords lock only the network they came from, for 15 minutes, with the same answer as a wrong name.

  • The plugin pins the server's identity key the first time (TOFU) and asks for a fresh signature with it before using the server; if the key changes, nothing is sent until you accept the new one with miblo server set (and your Miblo's code).

  • Keep secrets.json (or the Worker's secrets) out of backups others can read.

  • Found a vulnerability? Write to contato@miblo.ai (subject "Security"), privately, and do not publish it before the fix.

Reproducible builds

The build is deterministic (base image pinned by digest, dependencies from package-lock.json). The Docker image holds dist/HASHES.txt: the SHA-256 of every file served and of the server, and one hash of the set (build …):

docker compose exec relay tail -1 dist/HASHES.txt   # what is running
npm ci && npm run build && npm run hashes | tail -1  # what this source builds

Both must match: what runs is what this source, checked by its signature, builds.

Development

npm test runs the relay suite on both runtimes (workerd through Miniflare, and Node), the API tests, the Worker test and, with MIBLO_PLUGIN_DIR pointing at a Miblo plugin checkout's plugin/ folder, an end-to-end test of the real plugin against this server. npm run typecheck checks the types. The phone app under app/src is the same source as miblo.ai's phone app; app/shims and app/src/selfhost are what this build adds (see docs/sources.md (docs/sources.md)).

License

AGPL-3.0 (LICENSE): whoever offers a modified version of this server as a service must publish the source of their changes. The project owner can change the license while they are its only author.

Volver a la documentación