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
+3 -3
View File
@@ -1,6 +1,6 @@
"""First run: an account to sign in with.
An installation started from the command line is given no environment, so the
An instance started from the command line is given no environment, so the
superuser a deployment sets in `.env` has to come from somewhere. The session
key is the CLI's own business — it has to be set before this module can be
imported at all.
@@ -27,7 +27,7 @@ def ensure_superuser(
"""The account to sign in with, made on first run.
Returns the user and, when it was just created, the password in clear —
the caller prints it once. An installation that already has a superuser is
the caller prints it once. An instance that already has a superuser is
left alone: this is a first run, not a password reset.
"""
existing = session.exec(
@@ -57,7 +57,7 @@ def pick_superuser(session: Session, email: str | None = None) -> User:
select(User).where(User.is_superuser == True) # noqa: E712
).all()
if not users:
raise LookupError("This installation has no superuser to enrol as")
raise LookupError("This instance has no superuser to enrol as")
if len(users) > 1:
addresses = ", ".join(sorted(u.email for u in users))
raise LookupError(f"Several superusers here — name one with --as: {addresses}")
+5 -5
View File
@@ -54,7 +54,7 @@ class Settings(BaseSettings):
FRONTEND_HOST: str = "http://localhost:5173"
ENVIRONMENT: Literal["local", "staging", "production"] = "local"
#: Everything this installation keeps: the database, the flow repository,
#: Everything this instance keeps: the database, the flow repository,
#: secrets, artifacts and the user venv. The paths below derive from it
#: unless they are set explicitly.
DATA_DIR: Path = Path("flow-data")
@@ -72,13 +72,13 @@ class Settings(BaseSettings):
# Which failures reach which channel. Beside the flows, not in them:
# alerting is the deployment's concern, not any one flow's.
ALERTS_FILE: Path = Path("flow-data/alerts.json")
# This installation's web push keypair and the browsers subscribed to it.
# This instance's web push keypair and the browsers subscribed to it.
# Beside the alerts it serves; deleting it makes every device subscribe
# again.
WEBPUSH_FILE: Path = Path("flow-data/webpush.json")
# Where machines can be started from when a node needs one and nothing that
# could take it is attached. Operator-authored, like the alerts beside it,
# and absent on an installation that has nowhere to start one.
# and absent on an instance that has nowhere to start one.
PROVISIONERS_FILE: Path = Path("flow-data/provisioners.json")
# Which dashboards each device shows. Beside the flows for the same reason
# alerting is: where a screen hangs is the deployment's concern rather than
@@ -99,7 +99,7 @@ class Settings(BaseSettings):
PRIVATE_API_ENABLED: bool = False
DOMAIN: str = "localhost"
OAUTH_PRIVATE_KEY_FILE: Path = Path("flow-data/oauth-key.pem")
# Written only when someone enrols this installation with a portal.
# Written only when someone enrols this instance with a portal.
# Its absence is what keeps remote access off.
CLOUD_CONFIG_FILE: Path = Path("flow-data/cloud.json")
OAUTH_CODE_EXPIRE_SECONDS: int = 60
@@ -113,7 +113,7 @@ class Settings(BaseSettings):
# because a limit somebody set and did not get is the worse surprise.
FLOW_MAX_WORKERS: PositiveInt = 4
# How many cascades may be in flight at once. Sustained throughput is this
# over the mean cascade time, so an installation whose nodes wait on the
# over the mean cascade time, so an instance whose nodes wait on the
# network rather than on a CPU wants it higher than the core count.
FLOW_MAX_CASCADES: PositiveInt = 4
# How many batch runs are driven at once. A different limit from the one
+3 -3
View File
@@ -142,9 +142,9 @@ def init_db(session: Session) -> None:
user = crud.create_user(session=session, user_create=user_in)
#: The sizes an installation starts with. Written once, when there are none,
#: The sizes an instance starts with. Written once, when there are none,
#: and editable from there — what a name means is a property of the machines
#: this installation has, and nothing here knows what those are.
#: this instance has, and nothing here knows what those are.
SEED_FLAVORS: tuple[dict[str, Any], ...] = (
{"name": "small", "cpus": 1, "ram": 2048, "description": "A poll, a threshold"},
{"name": "medium", "cpus": 4, "ram": 8192, "description": "A step that computes"},
@@ -160,7 +160,7 @@ SEED_FLAVORS: tuple[dict[str, Any], ...] = (
def seed_flavors(session: Session) -> None:
"""Give a new installation sizes to pick from, once.
"""Give a new instance sizes to pick from, once.
Only when there are none at all: they are editable, and re-adding one that
somebody deliberately removed would be an argument nobody can win.