Rename Installation to Instance

Follows the portal: the noun is "instance" everywhere the app says it —
UI strings, CLI output, error details, docs and comments. The wire keys
(`instance_id`, `instance_token`) and the hub route this calls move with it.

An existing cloud.json is adopted rather than refused: without the key
alias the dataclass fails to parse, which the caller swallows and reads as
"never enrolled" instead of "reconnect".

`instance_key` on a node type becomes `target_key`. It means the outside
thing a node points at, which is a different sense of the word, and keeping
both would put two meanings of "instance" in one codebase.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YrQnKV3bnQd4K342y8tKj
This commit is contained in:
2026-08-31 10:12:01 +02:00
co-authored by Claude Opus 5
parent 6534855492
commit d01a8dad37
101 changed files with 374 additions and 375 deletions
+6 -6
View File
@@ -272,26 +272,26 @@ swap the device out without rebuilding what hangs there.
!!! note "If the link is wrong"
The pairing link is built from the installation's `FRONTEND_HOST`. If that
The pairing link is built from the instance's `FRONTEND_HOST`. If that
is not the address devices on your network actually reach, fix the setting
rather than the link: it is the same one password-reset mails and the OAuth
metadata are built from.
### A screen somewhere you cannot reach
Another building, someone else's network, no route in. An installation
Another building, someone else's network, no route in. An instance
[enrolled with a portal](portal.md) shows a second link,
`https://hub.${DOMAIN}/i/{installation-id}/panel`, and the same three steps
`https://hub.${DOMAIN}/i/{instance-id}/panel`, and the same three steps
work through it: the portal serves that one page without a session, forwards
the pairing calls down the tunnel, and mints the credential when you approve
the code. The pairing line then reads *via portal*.
The portal names the panel and nothing else. What the panel may read is decided
on the installation, on every call, by the same check a locally paired screen
passes. Two differences: it acts as the account the installation was enrolled
on the instance, on every call, by the same check a locally paired screen
passes. Two differences: it acts as the account the instance was enrolled
with rather than as whoever approved it, and deleting the panel stops it here
immediately while the portal's copy of the token expires on its own — which is
also why unpairing, which works on a credential this installation signed, does
also why unpairing, which works on a credential this instance signed, does
not reach a remote screen. Revoke that one at the hub.
## See also
+10 -10
View File
@@ -1,12 +1,12 @@
# The dashboard app
The web interface is a single-page app served at `app.${DOMAIN}` — or, for an
installation reached through a portal, at `${DOMAIN}/i/{installation-id}`.
instance reached through a portal, at `${DOMAIN}/i/{instance-id}`.
Either way it is the same application, and it is a client of the same REST API
you can script against.
Sign in with the account the installation was created with. On a fresh
installation that account was printed once, on the first start.
Sign in with the account the instance was created with. On a fresh
instance that account was printed once, on the first start.
## The shell
@@ -22,7 +22,7 @@ phone the sidebar collapses to a sheet.
| **Modules** | the Python packages your node code may import |
| **Alerts** | where failures get sent |
| **Admin** | users (superusers only) |
| **Search** | anything in this installation, by name |
| **Search** | anything in this instance, by name |
| **Settings** | your account, appearance, and remote access |
### Search
@@ -33,8 +33,8 @@ dashboards and the widgets on them, panels, secrets, modules, workers and alert
channels. Picking a node opens its flow with that node in focus; picking a
widget opens its dashboard.
It searches this installation. Reached through a portal, other installations
are behind **All installations** at the top of the sidebar.
It searches this instance. Reached through a portal, other instances
are behind **All instances** at the top of the sidebar.
## Home
@@ -44,7 +44,7 @@ The one screen you leave open. Four things share it.
Every flow drawn as a neuron, wired to the flows it exchanges messages with.
This is the brand mark made live, and it is also the fastest read on the
installation: a neuron pulses when its flow is running work, and its ring turns
instance: a neuron pulses when its flow is running work, and its ring turns
terracotta when the flow cannot run as written. A neuron with a problem keeps
its label showing so you can see which one it is without hovering.
@@ -70,7 +70,7 @@ Always answers, degraded or not. The tiles cover:
because their graph does not validate
- **Nodes** — how many failed to load
- **Runs running** — batch runs in flight right now, and how many are
waiting. Only on an installation that has run something
waiting. Only on an instance that has run something
- **Queue** — depth, and how old the oldest pending item is
- **Loop lag** — whether the engine's event loop is keeping up
@@ -110,8 +110,8 @@ so does Escape. The Dashboards screen works the same way.
- [Dashboards and panels](dashboards.md) — widgets, bindings, and hanging a
screen on a wall
- [Secrets, modules and alerts](operations.md) — the three screens that keep an
installation running
- [Accounts and the portal](portal.md) — reaching an installation from outside
instance running
- [Accounts and the portal](portal.md) — reaching an instance from outside
its network
## Appearance
+5 -5
View File
@@ -1,7 +1,7 @@
# Secrets, modules and alerts
Three screens that have nothing to do with each other except that an
installation you actually depend on needs all of them.
instance you actually depend on needs all of them.
## Secrets
@@ -18,7 +18,7 @@ have stored.
**Secrets** is where the values live. Add a name and a value; the value is
never shown again, and the list only ever shows names.
They are encrypted at rest with a key derived from the installation's
They are encrypted at rest with a key derived from the instance's
`SECRET_KEY`, and kept **outside** the flow repository. That matters because
flows are a git repository you may well push somewhere: what gets committed and
shared never contains a password.
@@ -35,7 +35,7 @@ authentication failure.
## Modules
Node code runs in a virtual environment of its own, on the installation's data
Node code runs in a virtual environment of its own, on the instance's data
volume — separate from the packages Fluksio itself runs on. A pin of yours can
never shadow one of ours, and vice versa.
@@ -69,7 +69,7 @@ where you say who hears about it.
| Kind | Settings |
|---|---|
| **ntfy** | server, topic, and a token for a protected topic |
| **SMTP** | an address to send to (the installation's mail settings do the rest) |
| **SMTP** | an address to send to (the instance's mail settings do the rest) |
| **Webhook** | a URL to POST to |
| **Dashboard** | a message name a notification widget reads |
@@ -126,6 +126,6 @@ are registered.
Your own account: name, email, password, and appearance (light, dark, or
follow the system).
**Remote access** is where an installation is paired with a portal, remote
**Remote access** is where an instance is paired with a portal, remote
users are admitted, and the link is cut again. That has [its own
page](portal.md).
+27 -27
View File
@@ -1,6 +1,6 @@
# Accounts and the portal
Fluksio is self-hosted by default. An installation runs offline, keeps its data
Fluksio is self-hosted by default. An instance runs offline, keeps its data
on its own disk, and never contacts anything unless you tell it to.
The **portal** is optional, and it exists to solve two specific problems:
@@ -8,10 +8,10 @@ The **portal** is optional, and it exists to solve two specific problems:
1. **Your machine has no inbound route.** A homelab behind CGNAT, a cluster
node with no open ports, a laptop. Opening one is work, and often not
allowed.
2. **You want a browser on it anyway.** A `pip install fluksio` installation
2. **You want a browser on it anyway.** A `pip install fluksio` instance
has no web server for the dashboard at all.
An enrolled installation dials *out* to the portal and holds one websocket
An enrolled instance dials *out* to the portal and holds one websocket
open. The portal serves the dashboard from its own side, and only the API calls
travel down the tunnel — so the interface loads at portal speed and your
machine stays unreachable from the internet.
@@ -19,18 +19,18 @@ machine stays unreachable from the internet.
## Enrolling
Two halves, deliberately: whoever performs the second step decides what the
installation's owner gets.
instance's owner gets.
**On the portal** (`hub.${DOMAIN}`, or [fluksio.com](https://fluksio.com) for
the hosted one): **Installations → Add installation**, give it a name, and copy
the hosted one): **Instances → Add instance**, give it a name, and copy
the code.
**On the installation**, either from the dashboard:
**On the instance**, either from the dashboard:
> **Settings → Remote access**, enter the portal URL and the code, press
> **Connect**.
or from the command line, which is the path for an installation with no web
or from the command line, which is the path for an instance with no web
interface of its own:
```sh
@@ -42,40 +42,40 @@ The code is single-use and expires in fifteen minutes.
A portal session then arrives as *that local account* — the settings screen
states this plainly, because it is the whole security model in one sentence.
Use `--as someone@example.com` to enrol as a specific local account when the
installation has several superusers.
instance has several superusers.
Once enrolled, the installation appears under **Installations** with its
Once enrolled, the instance appears under **Instances** with its
status, when it was last seen and its version. **Open** takes you to its
dashboard at `${DOMAIN}/i/{installation-id}`.
dashboard at `${DOMAIN}/i/{instance-id}`.
## What the portal can and cannot do
The portal holds one credential for your installation and proxies requests down
the tunnel. What those requests may do is decided **on the installation**, by
The portal holds one credential for your instance and proxies requests down
the tunnel. What those requests may do is decided **on the instance**, by
the same checks a local session passes.
The trust anchor is a signing keypair on the portal. Every installation pins
The trust anchor is a signing keypair on the portal. Every instance pins
its public half at enrolment and rejects anything else — which is what stops a
hijacked DNS entry or a mis-issued certificate from impersonating the portal.
## Letting someone else in
Anyone else on the portal reaches your installation only if a superuser there
Anyone else on the portal reaches your instance only if a superuser there
admits them, and they arrive as a local user of their own rather than as you.
1. **They**: **Installations → Join an installation**, and copy the code. It is
1. **They**: **Instances → Join an instance**, and copy the code. It is
bound to their portal account and expires in fifteen minutes.
2. **You**, on the installation: **Settings → Remote access → Add remote
2. **You**, on the instance: **Settings → Remote access → Add remote
user**, and enter the code.
3. They now see the installation under **Installations**, marked *Shared by*,
3. They now see the instance under **Instances**, marked *Shared by*,
with **Open** and nothing else. Renaming, re-keying and removing stay with
you.
The installation redeems that code against the portal using its own credential.
The instance redeems that code against the portal using its own credential.
A portal session cannot do this — which is what stops somebody you let in from
letting others in.
On the installation they appear under **Admin → Users**, badged *Portal*, never
On the instance they appear under **Admin → Users**, badged *Portal*, never
a superuser and with no password.
## Cutting it off
@@ -84,29 +84,29 @@ a superuser and with no password.
|---|---|---|
| The portal | **New code** | rotates the credential and drops the current link |
| The portal | **Remove** | deletes the registration and cuts the connection |
| The installation | **Disconnect** | unilateral and immediate — the portal's tokens stop verifying here whatever the portal still has on file |
| The installation | delete a user under **Admin → Users** | that one person, immediately, independent of the portal |
| The instance | **Disconnect** | unilateral and immediate — the portal's tokens stop verifying here whatever the portal still has on file |
| The instance | delete a user under **Admin → Users** | that one person, immediately, independent of the portal |
**New code** also cuts every credential the portal minted for this installation,
**New code** also cuts every credential the portal minted for this instance,
including [wall panels paired through it](dashboards.md#a-screen-somewhere-you-cannot-reach).
The installation's own **Disconnect** is the one to reach for if you are ever
The instance's own **Disconnect** is the one to reach for if you are ever
unsure: it does not need the portal's cooperation.
## Running your own portal
The portal is the `index` stack's `hub` service — accounts, the registry of
connected installations, and the websocket each one dials in on. Two things
connected instances, and the websocket each one dials in on. Two things
about it are load-bearing:
- **It runs a single process.** It keeps its attached installations in the
- **It runs a single process.** It keeps its attached instances in the
memory of the process holding their sockets, so a second worker would answer
for links it does not hold. Scaling out needs a routing layer first.
- **Back up the signing keypair with the database.** Replacing it forces every
installation to be enrolled again.
instance to be enrolled again.
Websocket upgrades must be enabled on the `hub` hostname in whatever proxy
fronts it. Without them every installation sits in a reconnect loop and the
fronts it. Without them every instance sits in a reconnect loop and the
portal shows them all offline.
## See also