479 lines
28 KiB
Markdown
479 lines
28 KiB
Markdown
<p align="center">
|
|
<img src="jarvis.svg" width="76" alt="Jarvis">
|
|
</p>
|
|
|
|
<h1 align="center">Jarvis</h1>
|
|
|
|
<p align="center"><strong>AI-assisted infrastructure administration for MSPs.</strong></p>
|
|
|
|
An operator opens a conversation and asks for the work in words. Jarvis does it: a command over SSH
|
|
or through an enrolled agent, a Proxmox guest powered on, a Microsoft 365 account blocked, a MikroTik
|
|
firewall read back — on the machines of one client organization, with credentials it decrypts from
|
|
the vault and shows nobody. Every call is judged before it runs, and the ones that cannot be undone
|
|
stop and ask a human in the thread.
|
|
|
|
The difference from an assistant that writes commands for you to paste is that these run.
|
|
|
|
This repository runs Jarvis from published container images — no source, no build, no account with
|
|
the project.
|
|
|
|
- [What it does](#what-it-does)
|
|
- [What decides whether a tool call runs](#what-decides-whether-a-tool-call-runs)
|
|
- [What it looks like](#what-it-looks-like)
|
|
- [How it works](#how-it-works)
|
|
- [What you need](#what-you-need)
|
|
- [Install](#install)
|
|
- [Your reverse proxy has two requirements](#your-reverse-proxy-has-two-requirements)
|
|
- [Upgrading](#upgrading)
|
|
- [The agent](#the-agent)
|
|
- [Things worth knowing before you trust it with production](#things-worth-knowing-before-you-trust-it-with-production)
|
|
- [When it does not come up](#when-it-does-not-come-up)
|
|
- [Backups, and restoring one](#backups-and-restoring-one)
|
|
- [Removing it](#removing-it)
|
|
- [Licence keys, and what your instance reports](#licence-keys-and-what-your-instance-reports)
|
|
- [Getting a licence, and getting help](#getting-a-licence-and-getting-help)
|
|
|
|
## What it does
|
|
|
|
**Five connectors reach a managed system.** Each one is a set of tools the assistant may call, and
|
|
what it may do with them is decided per call — see the next section.
|
|
|
|
| Connector | Reaches |
|
|
| --- | --- |
|
|
| **SSH** | Anything with a shell — Linux, Windows, and CLI-driven network gear. An appliance that serves no SFTP is offered the command tool alone, rather than four that would fail at the handshake. |
|
|
| **Jarvis agent** | A machine running the enrolled agent, which dials out — so it works behind NAT, on a dynamic address, with no inbound rule and no SSH exposed. Commands, files, services, processes, system facts. |
|
|
| **Proxmox VE** | The cluster API. Read and power only — no create, clone, snapshot, backup or migrate. |
|
|
| **MikroTik RouterOS** | The native binary API, or the RouterOS 7 REST API. One identical tool surface either way; the connection decides the transport. |
|
|
| **Microsoft 365** | Graph, app-only, on any Microsoft cloud, authenticating with a client secret or a certificate. Named tools for identity, licences, groups, admin roles, Exchange Online, Intune and the audit logs, plus one that reaches every remaining Graph endpoint. |
|
|
|
|
**An asset is the managed thing; a connection is a way of reaching it.** A Linux host commonly
|
|
answers on OpenSSH *and* through an enrolled agent; a Proxmox node has a cluster API, a shell and an
|
|
agent. Each route carries its own address, its own credentials and its own health — so a vendor API
|
|
that stops answering no longer makes a device unmanageable while its console is up.
|
|
|
|
**Documents.** A conversation accumulates what the assistant writes: reports and runbooks in
|
|
Markdown, diagrams in Mermaid, tabular data as a workbook. What it writes is always a *source*, and
|
|
Jarvis renders the file on download — PDF and Word from prose, Excel and CSV from a workbook, HTML
|
|
from either. Keeping the source is what leaves a document revisable instead of a dead binary.
|
|
|
|
**And what an MSP has to administer about itself**: organizations and members under a role ceiling,
|
|
invitations, an encrypted vault with its own folder tree and its own grants, an audit trail,
|
|
passkeys and TOTP, a notification centre, and a console in English, French or German — a property of
|
|
the account, not of the browser.
|
|
|
|
## What decides whether a tool call runs
|
|
|
|
Three things, resolved on every single call. The most restrictive wins.
|
|
|
|
**Risk is a property of the operation.** A tool declares a baseline and may escalate per invocation:
|
|
a shell command is mutating in general and destructive for `rm -rf`, `mkfs`, `shutdown`,
|
|
`iptables -F` and about two dozen other patterns. Escalation is one-way — a per-call assessment can
|
|
raise the risk, never lower it.
|
|
|
|
**Autonomy is a property of the conversation**, chosen by the operator:
|
|
|
|
| | Safe | Mutating | Destructive |
|
|
| --- | --- | --- | --- |
|
|
| **Read-only** | run | refuse | refuse |
|
|
| **Ask before every change** | run | ask | ask |
|
|
| **Ask before destructive changes** (default) | run | run | ask |
|
|
| **Full access** | run | run | run |
|
|
|
|
"Ask" raises an approval request in the conversation, and the run parks until a human answers. The
|
|
level is re-read on every call, so lowering it takes effect on the very next tool call of a run
|
|
already in flight. **Full access removes the last in-chat gate for every participant** — destructive
|
|
operations then run immediately, with no prompt and no second pair of eyes. Make it a deliberate
|
|
choice.
|
|
|
|
**Permission is a property of the person.** A grant says which slice of the asset tree somebody may
|
|
operate, through which connectors, up to which risk, and whether the tools whose operation the
|
|
assistant *composes* — a shell command, an arbitrary Graph request, the contents of a file — are
|
|
admitted at all. Grants resolve by walking outward from the asset: the most specific level that says
|
|
anything decides entirely, and if nothing has spoken by the root the answer is no. Absent means
|
|
nothing, so a forgotten grant fails closed rather than open.
|
|
|
|
> The conversation's autonomy is a **floor the operator imposes on themselves**; the grant is the
|
|
> **ceiling imposed on them**. What runs is whichever binds.
|
|
|
|
A conversation also carries a **scope** — any mix of assets and folders, a folder granting its whole
|
|
subtree. Scope is checked *before* the asset is resolved, so a machine out of scope never has its
|
|
vault secrets decrypted.
|
|
|
|
## What it looks like
|
|
|
|
<p align="center">
|
|
<img src="docs/img/console.png" alt="The Jarvis console, on a freshly installed instance" width="900">
|
|
</p>
|
|
|
|
The console on a fresh install. The footer names the two builds you are running, which is the first
|
|
thing to quote when something is wrong.
|
|
|
|
<p align="center">
|
|
<img src="docs/img/asset.png" alt="One asset, its two routes, its agent and its inventory" width="900">
|
|
</p>
|
|
|
|
One asset, and the distinction the model rests on: **two routes to the same machine**, each with its
|
|
own address, its own credentials and its own health. One is preferred and untested; the other is
|
|
disabled without being deleted, so its settings and its history survive and no tool may use it.
|
|
|
|
## How it works
|
|
|
|
Four containers. **Only `web` publishes a port**: its nginx serves the console and reverse-proxies
|
|
`/api` and the websocket to `api` over the internal network, so your TLS terminator has exactly one
|
|
target and the API is never reachable from outside the compose network.
|
|
|
|
| Service | What it is |
|
|
| --- | --- |
|
|
| `web` | nginx serving the React console. The only published port. |
|
|
| `api` | REST, auth, the vault, the connectors, the tool-calling loop, and the websocket. |
|
|
| `postgres` | Everything except the vault master key. |
|
|
| `redis` | Socket fan-out, nonces and rate-limit counters. |
|
|
|
|
The assistant streams a turn, executes the tool calls the model asked for **strictly after the
|
|
stream is fully drained**, then feeds the results back — up to fifty rounds per user turn. That
|
|
ordering is what makes retrying a broken stream safe: at the moment a stream fails, no tool of that
|
|
round has run, so replaying it re-generates intent and never re-runs an operation.
|
|
|
|
**A run that was in flight when the api stopped is picked up when it comes back.** A shutdown aborts
|
|
each loop and lets it write out what it had streamed with a note saying why the transcript ends
|
|
there; the next process finishes that message, closes any tool call whose outcome is unknown saying
|
|
in as many words that it is unknown, and resumes the run — instructed to read the current state
|
|
before repeating anything that writes. This is why the api asks for a stop grace period, and part of
|
|
why it must run as a single replica.
|
|
|
|
**The agent dials out.** Nothing inbound is opened on a managed machine. It holds a websocket to
|
|
your instance, signs each session with a key whose private half never leaves it, and reports its
|
|
inventory on every heartbeat.
|
|
|
|
## What you need
|
|
|
|
- Docker with Compose v2, on **x86-64 Linux**. The api and web images are published for
|
|
`linux/amd64` only, so an arm64 host — a Pi, an Ampere, a Graviton — fails at `docker compose pull`
|
|
with no matching manifest. The machines you *administer* have no such limit: the agent ships arm64
|
|
builds for Linux, macOS and Windows.
|
|
- A hostname and a TLS terminator in front of it. Jarvis speaks plain HTTP and does not manage
|
|
certificates.
|
|
- An API key for an OpenAI-compatible endpoint. The api will not start without one.
|
|
- A licence key. See [Licence keys](#licence-keys-and-what-your-instance-reports) — without one an
|
|
instance runs but creates nothing new.
|
|
- Roughly 2 GB of RAM for the stack and room for Postgres to grow.
|
|
|
|
## Install
|
|
|
|
```sh
|
|
git clone https://git.luxit.be/Luxit/jarvis-selfhost.git
|
|
cd jarvis-selfhost
|
|
cp .env.example .env
|
|
$EDITOR .env # every comment in it is load-bearing; the secrets note especially
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
First boot syncs the database schema and runs its data backfills before the API listens, so give it
|
|
about a minute. Then point your reverse proxy at `JARVIS_PORT` and open the app.
|
|
|
|
**Give Jarvis its public address with the right scheme.** `WEB_ORIGIN` is the CORS decision and it
|
|
accepts exactly one origin; `PUBLIC_URL` is what gets baked into agent install commands, invitation
|
|
links and share links. A wrong `WEB_ORIGIN` presents as a sign-in page that cannot sign in. A wrong
|
|
`PUBLIC_URL` does not fail at deploy time — it fails weeks later, on somebody else's server.
|
|
|
|
**The first account created becomes super-admin.** Self-registration is open by default, so sign up
|
|
immediately after the stack is up and then close registration under **Settings → Platform →
|
|
General**. Leaving it open means anyone who reaches the sign-in page gets an account.
|
|
|
|
## Your reverse proxy has two requirements
|
|
|
|
Both are the kind that produce confusing symptoms rather than clean errors.
|
|
|
|
- **Forward the WebSocket upgrade.** Two separate sockets ride `JARVIS_PORT`: the chat, on
|
|
`/socket.io/`, and enrolled agents, on `/api/agents/ws`. Neither falls back to plain HTTP. Without
|
|
the upgrade the chat does not lose streaming — it never connects at all, and since the prompt
|
|
itself travels over that socket, nothing sends. Presence and in-chat approvals go with it, and no
|
|
agent can connect.
|
|
- **Give it a long read timeout** — 300s or so. A reasoning model can go 90+ seconds without emitting
|
|
a byte, and a 60s default cuts the response mid-stream. The client sees a connection reset rather
|
|
than a timeout, which reads like a bug in Jarvis.
|
|
|
|
Forward `X-Forwarded-For` too, and set `TRUST_PROXY_HOPS` to match how many proxies rewrite it —
|
|
that is what puts real client addresses in the audit trail and the session list instead of your
|
|
proxy's.
|
|
|
|
## Upgrading
|
|
|
|
```sh
|
|
docker compose pull && docker compose up -d
|
|
```
|
|
|
|
That is the whole upgrade: the api, the web, and — with the agent overlay on — the agent release all
|
|
track the **`stable`** channel by default. Postgres and Redis are not on a Jarvis channel; they
|
|
follow their own upstream tags, `postgres:16-alpine` and `redis:7-alpine`. Schema changes apply
|
|
themselves when the api starts, and the api and the web are versioned independently — their numbers
|
|
are not meant to match, because usually only one side changed.
|
|
|
|
**A schema change is one-way.** Jarvis has no migration history: each boot force-syncs the database
|
|
to the schema its image carries, adding what a release added and dropping what it removed, without
|
|
prompting. Pulling an older api image does not undo that — unlike the image itself, the schema stays
|
|
where the newer build left it. Restoring a dump is the only way back, which is what makes the one
|
|
below a prerequisite rather than a precaution.
|
|
|
|
```sh
|
|
docker compose exec -T postgres pg_dump -U jarvis jarvis | gzip > jarvis-$(date +%F).sql.gz
|
|
```
|
|
|
|
### Channels
|
|
|
|
| Channel | What it means |
|
|
| -------- | ---------------------------------------------------------------------------------- |
|
|
| `stable` | Promoted after running on the publisher's own instance. **The default, and what you want.** |
|
|
| `dev` | Every build, as soon as it is published. Nothing has tried it yet. |
|
|
| `latest` | A second name for `stable`, kept so nothing that already used it has to change. |
|
|
|
|
A channel is a **pointer** and a version number is a **fact**. `0.78.2` means one specific set of
|
|
bytes for ever; `stable` means whichever set we currently stand behind, and it moves. A build only
|
|
reaches `stable` by being promoted — and promotion copies the manifest of an image that has already
|
|
been published and already run. It never rebuilds, so the bytes you receive are the same bytes that
|
|
were tested, not a fresh build of the same source.
|
|
|
|
**Channels do not mean the app forgets which build it is.** The version is stamped into the image
|
|
when it is built, so the footer in the app, `/version.json` and the agent manifest keep reporting the
|
|
real number whichever name you pulled it under. That is what lets you tell somebody which build you
|
|
are on when something goes wrong. Set `JARVIS_CHANNEL` in `.env` and the footer names your channel
|
|
beside those numbers; leave it empty if you pin, because then you follow no channel.
|
|
|
|
Once you are in production, consider pinning: set `JARVIS_IMAGE_API`, `JARVIS_IMAGE_WEB` and
|
|
`JARVIS_IMAGE_AGENT` in `.env` to explicit version tags. It makes an upgrade a decision rather than a
|
|
side effect of pulling. Pin the version you are *already running* — the footer shows it — rather than
|
|
one copied from a document, and remember that moving the api pin backwards runs an old build against
|
|
a schema that has already moved forward.
|
|
|
|
**Pin the agent one first if you pin only one.** The api and the web change what your own server
|
|
runs; the agent changes what runs on every machine you administer. Pinning the image decides which
|
|
build your instance publishes; a separate **update policy** decides when a machine takes it —
|
|
*As soon as available*, *On a schedule*, *Manually only*, *Let the agent decide* (the default, which
|
|
means on its next service start, and on a server that can be months) or *Never*. The instance-wide
|
|
answer is on **Settings → Platform → General**; an organization overrides it under **Settings →
|
|
Organization → Agent updates**, and a single machine overrides that. An enrolled agent also refuses
|
|
any version that is not strictly newer, so moving that pin back stops a rollout rather than reversing
|
|
it on machines that already took the update.
|
|
|
|
## The agent
|
|
|
|
**The Jarvis agent is an optional overlay, off by default.** Enrolling a machine downloads a compiled
|
|
binary that the api serves from `AGENT_RELEASE_DIR`, and a compose-only deployment has no way to
|
|
produce one. `docker-compose.agent.yml` supplies it as a pullable image instead. Leaving it off is a
|
|
supported state rather than a broken one: everything else works, the manifest and download endpoints
|
|
answer 503 saying no build is published, and the SSH, Proxmox, Microsoft 365 and MikroTik connectors
|
|
all reach machines without it.
|
|
|
|
Turn it on by adding one line to `.env`, so that every later `docker compose` command picks up both
|
|
files with no extra flags:
|
|
|
|
```sh
|
|
COMPOSE_FILE=docker-compose.yml:docker-compose.agent.yml
|
|
```
|
|
|
|
then `docker compose pull && docker compose up -d`. A one-shot `agent-releases` service copies the
|
|
release into a volume the api reads, and exits. From there, the **Agents** page in the sidebar issues
|
|
the install command: click **New install command**, pick Linux, macOS or Windows, and copy the one
|
|
line. (Settings → Organization → Agent updates is a different screen — it schedules how
|
|
already-enrolled agents take new builds.)
|
|
|
|
Four things worth knowing about it:
|
|
|
|
- **Until that publisher exits cleanly, the api does not start.** That is deliberate — a release that
|
|
failed to arrive should stop the deploy loudly rather than leave you handing 404s to every installer
|
|
you run this week. The cost is that an unreachable registry blocks the whole stack. The comment in
|
|
the file names the three lines to drop if you would rather it degraded quietly.
|
|
- **Upgrading it does not restart anything.** The api re-checks the file on disk on every download
|
|
request and re-hashes it whenever its size or timestamp has changed, so a new release in the volume
|
|
is served immediately and the published checksum always describes the bytes actually being served.
|
|
- **The agent version is its own number.** It moves independently of the api and the web, and a Jarvis
|
|
release usually does not touch it at all. The app footer does not carry it either — each enrolled
|
|
machine reports the build it runs, on the Agents page. Pin it with `JARVIS_IMAGE_AGENT`.
|
|
- **It carries an interactive shell.** Beyond what the assistant can do with it, a person gets a real
|
|
terminal on an enrolled machine from the browser — Linux, macOS and Windows alike. Sessions are
|
|
recorded by default, encrypted under `VAULT_MASTER_KEY` and deleted on a retention policy you set
|
|
under **Settings → Organization → Terminal sessions**. Turning recording off stops the transcript,
|
|
never the audit entry.
|
|
|
|
**What an enrolled agent can do.** Inventory the machine, run commands, read, write and fetch files,
|
|
list and control services, list processes, update itself, and carry that shell. It runs as root on
|
|
Linux and macOS and as LocalSystem on Windows, deliberately — its purpose is to administer the
|
|
machine.
|
|
|
|
What the **assistant** does with that reach is bounded by the autonomy policy and the approval gates
|
|
above. **The interactive shell is not.** There is no command to inspect before a shell opens, so the
|
|
risk ceiling has nothing to weigh; it is gated instead by a permission and a per-machine switch on
|
|
the grant. Decide who holds those before enrolling anything you care about.
|
|
|
|
## Things worth knowing before you trust it with production
|
|
|
|
These are deliberate and documented rather than surprises waiting to be found.
|
|
|
|
- **`VAULT_MASTER_KEY` has no recovery.** Read the note in `.env.example`. A database backup does not
|
|
protect what it seals — the backup holds ciphertext encrypted under that key, and that covers more
|
|
than the vault: the outbound-mail client secret, this instance's licence identity key, every TOTP
|
|
secret and every terminal recording go with it.
|
|
- **SSH host keys are not verified.** Every SSH connection trusts whatever key answers. This is the
|
|
one gap in the execution path with no compensating control.
|
|
- **The api must run as a single replica.** In-flight runs, pending approvals, presence and the
|
|
websocket of every enrolled agent live in one process's memory. The Redis in this stack does not
|
|
lift that limit: it fans outgoing events out to other replicas, but an incoming one is only ever
|
|
handled by the replica holding that connection. So a cancel or an approval answered on the wrong
|
|
replica is silently dropped, and an agent tool call can land on a replica that does not hold the
|
|
target machine. Worse, a starting replica's recovery sweep claims every run it does not own — so a
|
|
second instance re-executes, against your real infrastructure, operations the first is still
|
|
running.
|
|
- **There is no rate limiting on sign-in.** No throttler, no account lockout, and authentication
|
|
events are not audited. Credential stuffing is bounded only by the reverse proxy you put in front,
|
|
which this repository does not ship. If your proxy can rate-limit one route, make it that one.
|
|
- **An access token dies with its session, with one exception.** Every access token carries the id of
|
|
the session that issued it and every request re-checks that the session is live, so revoking a
|
|
session, signing other devices out or deactivating an account cuts that token off on its next
|
|
request. The exception is changing your own password: it revokes every *other* session and keeps
|
|
the one you are changing it from, so a token stolen from that session stays valid until it expires
|
|
— 15 minutes by default (`JWT_ACCESS_TTL`). An admin-forced reset drops every session.
|
|
- **The api container runs as root**, and so does nginx's master process in the web container,
|
|
though its workers drop privileges. Neither image declares a `USER`.
|
|
- **`/api/health` answers 200 with `status: "degraded"`** when the database is unreachable, so the
|
|
container healthcheck alone is not a liveness signal for the database.
|
|
- **Outbound mail is Microsoft Graph only.** There is no SMTP and no environment variable for any of
|
|
it: an app registration with `Mail.Send` and a shared mailbox, set up on the **Mail** tab of
|
|
**Settings → Platform → General**. Without it, invitations still work — the link comes back to the
|
|
admin who created it instead of being emailed. Nothing else is emailed: there is no password reset
|
|
and no address verification.
|
|
- The assistant executes real operations on real infrastructure. What stops for a human is the
|
|
conversation's autonomy level, and at *Full access* nothing does. See
|
|
[What decides whether a tool call runs](#what-decides-whether-a-tool-call-runs).
|
|
|
|
## When it does not come up
|
|
|
|
```sh
|
|
docker compose ps # who is running, and who is restarting
|
|
docker compose logs -f api # the api says why it refused to start
|
|
```
|
|
|
|
The api validates its configuration at boot and **fails fast rather than starting half-configured**,
|
|
so a first-boot failure is almost always one line of `.env`:
|
|
|
|
| Symptom | Cause |
|
|
| --- | --- |
|
|
| `docker compose up` exits complaining about a variable | That variable is empty in `.env`. Compose refuses before any container starts. |
|
|
| api restarts in a loop, logs `Invalid environment configuration` | A value is present but wrong. `VAULT_MASTER_KEY` must base64-decode to exactly 32 bytes. |
|
|
| The sign-in page loads but cannot sign in | `WEB_ORIGIN` is not the address the browser used, scheme included. It accepts one origin, not a list. |
|
|
| Chat never answers and nothing streams | The WebSocket upgrade is not being forwarded. |
|
|
| An answer dies part-way through, every time | The proxy's read timeout is too short. |
|
|
| Everything works but nothing can be created | No licence key. See below. |
|
|
| An agent installer answers 503 | No agent build is published — the overlay is off. That is a supported state. |
|
|
|
|
The api takes about a minute on first boot, syncing the schema before it listens. `docker compose ps`
|
|
showing `health: starting` for that long is expected, not a fault.
|
|
|
|
## Backups, and restoring one
|
|
|
|
Postgres holds everything except the vault key — avatars, documents, exports and terminal recordings
|
|
are all rows, not files on disk. **Of the stack's volumes, only `postgres_data` is worth backing
|
|
up.** `redis_data` carries nonces, rate-limit counters and the socket fan-out, all of which rebuild
|
|
themselves; `agent_releases` is refilled by the next `docker compose pull`.
|
|
|
|
```sh
|
|
docker compose exec -T postgres pg_dump -U jarvis jarvis | gzip > jarvis.sql.gz
|
|
```
|
|
|
|
Plus `VAULT_MASTER_KEY`, stored somewhere that is not this host. A dump without the key is a database
|
|
whose credentials cannot be read — and an instance restored under a *different* key keeps **looking**
|
|
configured, because nothing on a settings page decrypts anything. It fails on every reveal instead.
|
|
|
|
To restore, stop the api so nothing writes while you work, then load the dump into an empty database:
|
|
|
|
```sh
|
|
docker compose stop api web
|
|
docker compose exec -T postgres psql -U jarvis -d postgres \
|
|
-c 'DROP DATABASE IF EXISTS jarvis;' -c 'CREATE DATABASE jarvis;'
|
|
gunzip -c jarvis.sql.gz | docker compose exec -T postgres psql -U jarvis -d jarvis
|
|
docker compose start api web
|
|
```
|
|
|
|
Restore under the **same `VAULT_MASTER_KEY`** the dump was taken with. That value is not in the dump,
|
|
and no part of the restore will warn you that it differs.
|
|
|
|
## Removing it
|
|
|
|
```sh
|
|
docker compose down # stops everything, keeps the data
|
|
docker compose down -v # also deletes the volumes — every conversation, asset and credential
|
|
```
|
|
|
|
`down -v` is not recoverable from anything but a dump you already took.
|
|
|
|
## Licence keys, and what your instance reports
|
|
|
|
**Jarvis needs a licence key.** Ask for one (see [below](#getting-a-licence-and-getting-help)) and
|
|
put it in `.env` as `JARVIS_LICENSE_KEY`. The key is a signed token your instance verifies
|
|
**offline** — it carries your term and your limits, and it needs no network to be checked.
|
|
|
|
Without one, an instance keeps running everything already set up — every organization, every user,
|
|
every agent, every asset, and the assistant itself — and refuses only to create NEW ones: no new
|
|
organization, user, agent or asset. So an existing deployment does not stop working when this reaches
|
|
it, and a fresh install gets as far as its first administrator account and then needs a key.
|
|
|
|
**An unlicensed instance contacts nobody.** No check-in, no telemetry, nothing leaves your network at
|
|
all. The reporting below starts only once a key is in place.
|
|
|
|
A licensed instance then reports to the address written into that key, at an interval your provider
|
|
sets — every ten minutes on the current arrangement, so that a renewal or a revocation reaches you
|
|
promptly rather than tomorrow. This is everything it sends, in full:
|
|
|
|
| Field | What it is |
|
|
| ------------------------ | ------------------------------------------------- |
|
|
| Product | The literal string `jarvis` |
|
|
| Licence id | Which licence this is |
|
|
| Instance id + public key | A key pair your instance generated, identifying it |
|
|
| Version | Which Jarvis build you are running |
|
|
| Contract version | Which set of limits this build understands — a checksum, not a document |
|
|
| Counts | How many organizations, users, agents and assets |
|
|
| Public URL | Your instance's address — always sent, see below |
|
|
| Timestamps | When the process started, and when it reported |
|
|
| Signature + nonce | Proof the message came from this instance, and a one-time value so an old one cannot be replayed. Carries nothing about you. |
|
|
|
|
**Counts, not contents.** No names, no email addresses, no conversation text, no asset inventory, no
|
|
credentials, nothing about what you administer.
|
|
|
|
**The public URL is the one field that names your network rather than measuring something, and on a
|
|
licensed instance it is sent.** It used to be a switch on the licence screen; it is not any more,
|
|
because an installation the publisher can identify only by a fingerprint is one where "which of these
|
|
is the customer calling about" has no answer. The screen shows you the exact address that leaves,
|
|
under **Settings → Platform → Licence**. If that is not acceptable for your deployment, the answer is
|
|
to run unlicensed — which contacts nobody at all — or to take it up with us before installing.
|
|
|
|
The reply can carry a renewed key, which your instance adopts on its own — so a renewal reaches you
|
|
without anybody re-pasting anything.
|
|
|
|
**Your platform does not stop working because of a licence.** Expiry gives you a grace period — as
|
|
long as your key says, which on the current plans is 30 days, and none at all on a trial. After it,
|
|
Jarvis refuses only the creation of new organizations, users, agents and assets; everything already
|
|
set up keeps running, and so does the assistant. There is no state in which Jarvis disables, deletes
|
|
or locks you out of something you are already using. If the check-in cannot reach the server, nothing
|
|
changes at all: the key you hold is what governs, and it is checked without a network.
|
|
|
|
## Getting a licence, and getting help
|
|
|
|
**Antoine Cavelier — <antoine@luxit.be>.** Licence keys, pricing, and anything wrong with the
|
|
product.
|
|
|
|
When something is broken, the two facts worth putting in the first message are the build you are on
|
|
and what the api said:
|
|
|
|
```sh
|
|
curl -s https://your-jarvis.example.com/version.json # the web and api versions
|
|
docker compose logs --tail=100 api
|
|
```
|
|
|
|
`/version.json` is public on purpose, so you can quote it without signing in.
|
|
|
|
## Licence
|
|
|
|
The images are provided as-is with no warranty, no support and no commitment to future availability.
|
|
The source is not public and no rights to it are granted. Ask before deploying this commercially or
|
|
for third parties.
|