Manta is a self-hosted fork of Orca (MIT, © Lovecast Inc.).
Features:
- Self-host relay server
- No mandatory cloud account
- Internationalization
- Enterprise deployment
Based on: https://github.com/stablyai/orca
Builds are published under Releases; building from source works too.
There is no Manta cloud service. Sign-in and relay stay off until you point
them at your own deployment in Settings → Advanced → Manta Cloud endpoints;
relay-server/ is the server to run. Artifact publishing and feedback have no
settings pane and no implementation in relay-server/ — they read
MANTA_ARTIFACTS_API_URL and MANTA_FEEDBACK_API_URL, and stay off until
those point at services you supply. In-app documentation links go to upstream's
site, which describes the same features under their name.
Requires Node 20+ and pnpm.
pnpm install
pnpm dev # run the desktop app
pnpm build:mac # or build:win / build:linuxThe mobile companion lives in mobile/ and is an Expo app:
cd mobile && pnpm install && npx expo run:ios # or run:androidThe relay is what lets the phone reach a desktop that is not on the same network. It is a separate deployable — the desktop only ever talks to it over the network — and it is optional: on one LAN the phone pairs directly.
One relay serves several desktops. Each is identified by a hash of its own key, so a phone paired to one cannot reach another.
On a host with a domain pointing at it:
git clone https://github.com/paidaxingyo666/Manta /opt/manta
cd /opt/manta/relay-server/deploy
cp .env.example .env
$EDITOR .env
docker compose up -d.env needs four values, all of them required — compose refuses to start
rather than fall back to something insecure:
| Variable | What it is |
|---|---|
RELAY_DOMAIN |
the domain Caddy gets a certificate for |
RELAY_PORT |
the public port; 443 unless it is unusable on that host |
MANTA_RELAY_ENROLLMENT_SECRET |
what a desktop presents to enrol; without it the endpoint is open to anyone who can reach it |
MANTA_RELAY_TOKEN_SECRET |
signs relay tokens; leave it empty and every restart invalidates the ones already issued |
Both secrets: openssl rand -base64 32.
Caddy terminates TLS and proxies to the relay, which is never published on the host itself. The relay container runs unprivileged with a read-only root filesystem.
Prebuilt image, or build it yourself. Left as it is, compose compiles the
checkout — which a small VPS takes minutes to do, and needs a toolchain the
host may not have. To pull a published image instead, set RELAY_IMAGE in
.env:
# Docker Hub
RELAY_IMAGE=paidaxingyo666/manta-relay:1.1.0-dev.1
# Aliyun (Shanghai) — same image; one build pushes to both, so the digest matches
RELAY_IMAGE=crpi-b5cuqx1nkkudw599.cn-shanghai.personal.cr.aliyuncs.com/manta-relay/manta-relay:1.1.0-dev.1Both carry linux/amd64 and linux/arm64; docker pull picks the right one.
Pin a version rather than :latest — a relay that changes underneath a restart
is a bad surprise.
Accounts need 1.1.0 or newer. The 1.0.0 image predates them: it answers 404
on every account endpoint, so sign-in and the machine list are simply absent
against it. Building from source — the default — always matches the checkout.
1.1.0-dev.1 is a prerelease, which is what the pins above name because it is
what is published today. It never carries :latest, so a bare docker pull
still gets the last stable image. 1.1.0 replaces it once the account layer has
run somewhere real for a while.
Building from source stays the right answer when you are running an unreleased commit, or would rather not take a binary someone else built.
To check an image before deploying it:
docker run --rm -p 8787:8787 \
-e MANTA_RELAY_PUBLIC_URL=http://127.0.0.1:8787 \
-e MANTA_RELAY_TOKEN_SECRET="$(openssl rand -base64 32)" \
paidaxingyo666/manta-relay:1.1.0-dev.1
curl localhost:8787/health
# {"ok":true,"version":"1.1.0-dev.1","revision":"a1b2c3d…","builtAt":"2026-08-21T14:00:04Z"}That is a smoke test, not a deployment — no enrolment secret and no TLS, so it will not enrol anyone. Use compose for the real thing.
Settings → Advanced → Manta Cloud → Self-hosted server → Configure endpoints:
| Field | Value |
|---|---|
| Sign-in server | https://relay.example.com |
| Relay address | https://relay.example.com |
| OAuth client ID | manta-desktop |
| Enrolment secret | the value of MANTA_RELAY_ENROLLMENT_SECRET |
Include the port if the relay is not on 443. The origin is signed into every
host challenge byte for byte, so https://host and https://host:9443 are
different identities and a mismatch fails the handshake.
Applying signs the app out and relaunches it — a session issued by one deployment means nothing to another.
Repeat on each desktop. They share one enrolment secret.
By default a relay serves one identity: everyone who holds the enrolment secret is the same person, there is nothing to sign in to, and the desktop connects with no account at all. For your own relay that is usually what you want, and it is what every relay was before accounts existed.
Set MANTA_RELAY_ACCOUNTS=per-user when several people share the relay. Then
each person registers, and each gets their own identity and their own machines —
Settings → Manta Account → email and password, or Create one on this
relay. Accounts live on your relay and nowhere else; the relay is the only
thing that ever sees the password.
It is one or the other, chosen at deploy time. A relay that accepted both would let one careless click put someone on the shared identity, where their machines are everyone's — so the desktop asks the relay which it is and draws only that screen.
Signing in to the same account from a second computer puts both in one place: Your machines lists every desktop on the account, which is online now, and when each was last seen. That is also what keeps a per-user relay honest — a host belongs to exactly one account from the moment it is claimed, and another account asking for a token for it is refused.
Who may register is MANTA_RELAY_ALLOW_REGISTRATION; unset, it inherits the
enrolment secret, which is what a relay on the open internet wants. A relay
that already ran before accounts existed keeps working untouched — leaving
MANTA_RELAY_ACCOUNTS unset is exactly what it was. Switching it to per-user
later strands nothing: every host still belongs to the old shared identity, and
each desktop takes its own back — with the enrolment secret it already holds —
the first time someone signs in on it.
Settings → Mobile on the desktop shows a QR code. Scan it from the app.
Full reference — configuration, TLS on a non-standard port, observability,
operating notes: relay-server/README.md.
See CONTRIBUTING.md. Design and platform rules that apply to every change are in AGENTS.md.
MIT — see LICENSE. Upstream Orca's copyright is retained there alongside this fork's.
Upstream Orca's contributors wrote the code this builds on. Its Discord and @orca_build are upstream's, not this fork's — for Manta, open an issue here.