Merge pull request 'docs(installer): record Phase 0.2 — the sidecar's CLI and settled data paths' (#84) from docs/installer-phase-0.2 into main
Reviewed-on: #84 Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
This commit is contained in:
@@ -7,11 +7,11 @@ not match how `servuo-plugins` actually ships (see
|
|||||||
|
|
||||||
| Phase 0 item | State |
|
| Phase 0 item | State |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 0.1 `servuo-plugins` release workflow | 🟨 In review — [servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7) |
|
| 0.1 `servuo-plugins` release workflow | ✅ Merged — [servuo-plugins#7](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/7) + [#8](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/8); first overlay release is [`v0.1.1`](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/releases/tag/v0.1.1) |
|
||||||
| 0.2 `link` installable (data paths + `--print-config`) | ⬜ Not started |
|
| 0.2 `link` installable (data paths + `--print-config`) | 🟨 In review — [link#24](https://gitea.whitlocktech.com/RunicGateway/link/pulls/24) |
|
||||||
| 0.3 Bundle CI in the installer repo | ⬜ Not started |
|
| 0.3 Bundle CI in the installer repo | ⬜ Not started — **next**; both components it composes now exist |
|
||||||
| 0.4 This file + `INSTALL.md` | 🟦 This file exists; `INSTALL.md` waits on the shape settling |
|
| 0.4 This file + `INSTALL.md` | 🟦 This file exists; `INSTALL.md` waits on the shape settling |
|
||||||
| — Repo bootstrap (governance + CI) | 🟨 [`RunicGateway/installer`](https://gitea.whitlocktech.com/RunicGateway/installer) created; workflows in [installer#1](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/1) |
|
| — Repo bootstrap (governance + CI) | ✅ [`RunicGateway/installer`](https://gitea.whitlocktech.com/RunicGateway/installer) created; workflows merged ([installer#1](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/1), [#2](https://gitea.whitlocktech.com/RunicGateway/installer/pulls/2)) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -114,9 +114,13 @@ Two files, two owners:
|
|||||||
| `/etc/runicgateway/sidecar.toml` | uo-link | The sidecar's own schema, unchanged. Service sets `UOLINK_CONFIG` to this path |
|
| `/etc/runicgateway/sidecar.toml` | uo-link | The sidecar's own schema, unchanged. Service sets `UOLINK_CONFIG` to this path |
|
||||||
| `/etc/runicgateway/install.json` | installer | Deployed versions, file hashes, applied patches, ServUO path, timestamps |
|
| `/etc/runicgateway/install.json` | installer | Deployed versions, file hashes, applied patches, ServUO path, timestamps |
|
||||||
|
|
||||||
**Working-directory trap:** the sidecar writes both `sidecar.toml` and `uo-link.db` relative to CWD.
|
**Working-directory trap:** the sidecar wrote both `sidecar.toml` and `uo-link.db` relative to CWD.
|
||||||
Under `C:\Program Files\` that fails or silently lands in VirtualStore. The service definitions must
|
Under `C:\Program Files\` that fails or silently lands in VirtualStore. Phase 0.2 fixed the second
|
||||||
pin `UOLINK_CONFIG` and `UOLINK_DB_PATH` explicitly:
|
half in the sidecar — a relative `[store].path` now resolves against the directory holding
|
||||||
|
`sidecar.toml`, so pinning the config alone is enough to put the database somewhere deterministic —
|
||||||
|
but the config path itself is still CWD-relative by default, and "deterministic" is not the same as
|
||||||
|
"where this install wants it". The service definitions therefore still pin `UOLINK_CONFIG` and
|
||||||
|
`UOLINK_DB_PATH` explicitly:
|
||||||
|
|
||||||
- Linux: config `/etc/runicgateway/sidecar.toml`, db `/var/lib/runicgateway/uo-link.db`, dedicated
|
- Linux: config `/etc/runicgateway/sidecar.toml`, db `/var/lib/runicgateway/uo-link.db`, dedicated
|
||||||
service user
|
service user
|
||||||
@@ -131,6 +135,12 @@ becomes write-only. This is the largest "I installed it and nothing happened" fa
|
|||||||
|
|
||||||
The installer closes it by printing a copy-paste block at the end of a successful run — see §6.
|
The installer closes it by printing a copy-paste block at the end of a successful run — see §6.
|
||||||
|
|
||||||
|
Phase 0.2 supplied the missing half of that: `uo-link-sidecar --print-config` provisions the config
|
||||||
|
if absent and prints the resolved settings — token, both binds, `ws_path`, protocol version, db
|
||||||
|
path — as JSON. The installer reads the block it prints out of that one call. **It never parses the
|
||||||
|
log**, which was the alternative and would have made the handoff depend on a log format that is not
|
||||||
|
a contract.
|
||||||
|
|
||||||
### 2.5 `deploy.ps1` cannot be the cross-platform deployer
|
### 2.5 `deploy.ps1` cannot be the cross-platform deployer
|
||||||
|
|
||||||
It is PowerShell-only; a Linux ServUO host running .NET typically has no `pwsh`. It also hard-throws
|
It is PowerShell-only; a Linux ServUO host running .NET typically has no `pwsh`. It also hard-throws
|
||||||
@@ -255,6 +265,35 @@ Repo work that must land before an installer can exist.
|
|||||||
2. **`link`: make the sidecar installable.** Confirm/settle default data paths, and add a way to
|
2. **`link`: make the sidecar installable.** Confirm/settle default data paths, and add a way to
|
||||||
read back config non-interactively (e.g. `--print-config` emitting JSON: bind addresses, token,
|
read back config non-interactively (e.g. `--print-config` emitting JSON: bind addresses, token,
|
||||||
protocol version, db path) so the installer does not have to scrape logs for the token.
|
protocol version, db path) so the installer does not have to scrape logs for the token.
|
||||||
|
|
||||||
|
As built ([link#24](https://gitea.whitlocktech.com/RunicGateway/link/pulls/24)) — the sidecar
|
||||||
|
had **no CLI at all** before this, so the shape was chosen rather than inherited:
|
||||||
|
|
||||||
|
- **Four flags, hand-rolled:** `--print-config`, `--config <PATH>`, `--version`, `--help`. No
|
||||||
|
argument-parsing crate — it would be larger than the code it replaced — and deliberately no
|
||||||
|
flags that duplicate a config key, so `sidecar.toml` stays the single place settings live.
|
||||||
|
An unrecognized argument exits `2`; silently ignoring a typo'd flag would start a sidecar that
|
||||||
|
is not the one the installer asked for.
|
||||||
|
- **`--print-config` performs first-run setup rather than only reporting.** It runs the same
|
||||||
|
load path a normal start does, so a missing config file is written and a blank token is
|
||||||
|
generated and saved. That collapses "provision the sidecar" and "find out its token" into one
|
||||||
|
non-interactive call — which is exactly the sequence §6 needs. `config_created` and
|
||||||
|
`token_generated` say whether *this* run did either, because the values alone cannot
|
||||||
|
distinguish a fresh install from a re-read of an existing one, and a re-run must not report a
|
||||||
|
token as newly minted.
|
||||||
|
- **The document is the whole of stdout.** The log subscriber writes to stdout, so it is not
|
||||||
|
started in this mode. `ws_path` is emitted from the same constant the route is registered
|
||||||
|
with, so the installer's WebSocket URL cannot drift from the server's.
|
||||||
|
- **Relative `[store].path` now anchors to the config file's directory, not the CWD** — see
|
||||||
|
§2.3, which this half-closes on the sidecar side. Absolute paths are used as written; parent
|
||||||
|
directories are created; `:memory:` and `file:` URIs are left alone.
|
||||||
|
- **The db path is handed to sqlx as a path, not a `sqlite://` URL.** The URL spelling is
|
||||||
|
parsed as one: it percent-decodes the path and splits it on `?`, so an installed path
|
||||||
|
containing `%20` opened a different file than the operator named.
|
||||||
|
- **No platform data directories are compiled in.** That is the "settle" half of this item, and
|
||||||
|
the answer is that the *installer* owns layout (§2.3) and pins `UOLINK_CONFIG` /
|
||||||
|
`UOLINK_DB_PATH` in the service definition. Baking `/etc` and `%ProgramData%` defaults into
|
||||||
|
the binary would give the same paths two owners and break `cargo run` in a working tree.
|
||||||
3. **Bundle CI in the installer repo** (§7). Compose job (read both repos' latest releases → run the
|
3. **Bundle CI in the installer repo** (§7). Compose job (read both repos' latest releases → run the
|
||||||
two gates → publish `bundle.json`), the nightly cron, and the dispatch step appended to each
|
two gates → publish `bundle.json`), the nightly cron, and the dispatch step appended to each
|
||||||
component's release workflow. This must exist before Phase 1 is useful, since the installer
|
component's release workflow. This must exist before Phase 1 is useful, since the installer
|
||||||
@@ -279,7 +318,10 @@ Repo work that must land before an installer can exist.
|
|||||||
- Windows: `%ProgramFiles%\RunicGateway\`, data in `%ProgramData%\RunicGateway\`, service
|
- Windows: `%ProgramFiles%\RunicGateway\`, data in `%ProgramData%\RunicGateway\`, service
|
||||||
registration with automatic start and restart-on-failure.
|
registration with automatic start and restart-on-failure.
|
||||||
- Both: `UOLINK_CONFIG` and `UOLINK_DB_PATH` pinned in the service definition (§2.3).
|
- Both: `UOLINK_CONFIG` and `UOLINK_DB_PATH` pinned in the service definition (§2.3).
|
||||||
- Token surfacing (§6).
|
- Token surfacing (§6): run the installed binary once as
|
||||||
|
`uo-link-sidecar --print-config --config <the pinned path>` **before** registering the service.
|
||||||
|
That both writes the config the service will read and returns the token to print, so the service
|
||||||
|
never starts against a config that does not exist yet.
|
||||||
|
|
||||||
### Phase 3 — patch tier (opt-in)
|
### Phase 3 — patch tier (opt-in)
|
||||||
|
|
||||||
@@ -304,6 +346,12 @@ degrade loudly rather than silently.
|
|||||||
The last check matters most: it is the only thing that distinguishes "files copied" from "the bridge
|
The last check matters most: it is the only thing that distinguishes "files copied" from "the bridge
|
||||||
actually works" (§2.1).
|
actually works" (§2.1).
|
||||||
|
|
||||||
|
Three of those rows are answered by the sidecar's own CLI rather than by inspecting the filesystem:
|
||||||
|
`--version` prints `uo-link-sidecar <ver> (protocol <n>)`, and `--print-config` gives the config and
|
||||||
|
db paths the *installed service* resolves — so `doctor` reports what the binary would actually do,
|
||||||
|
not what `install.json` believes it was told to do. The protocol row compares that number against
|
||||||
|
the overlay manifest's declared one (§7.0).
|
||||||
|
|
||||||
`runicgateway update` — resolves the current bundle (§7.1), then acts asymmetrically by component,
|
`runicgateway update` — resolves the current bundle (§7.1), then acts asymmetrically by component,
|
||||||
deliberately:
|
deliberately:
|
||||||
|
|
||||||
@@ -355,11 +403,20 @@ Paste these into Admin → Shard on your Runic Gateway site:
|
|||||||
The token is write-only once saved — the site will never show it back to you.
|
The token is write-only once saved — the site will never show it back to you.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Every value in that block except the host and the site URL comes from one
|
||||||
|
`uo-link-sidecar --print-config` call (§2.4): `web.auth_token`, `protocol`, and `web.bind` +
|
||||||
|
`web.ws_path` for the two URLs. Only the **host** is substituted — `web.bind` is frequently
|
||||||
|
`0.0.0.0`, which is not something to hand a website — so the installer composes the URLs from the
|
||||||
|
host it detects or prompts for, rather than echoing the bind address.
|
||||||
|
|
||||||
The installer prompts for the site URL only to build that link; it never contacts the website. A
|
The installer prompts for the site URL only to build that link; it never contacts the website. A
|
||||||
future "installer registers itself with the website" flow (claim code + authenticated endpoint) is
|
future "installer registers itself with the website" flow (claim code + authenticated endpoint) is
|
||||||
explicitly **out of scope** — it is real backend work in a security-sensitive area and can be added
|
explicitly **out of scope** — it is real backend work in a security-sensitive area and can be added
|
||||||
later without changing anything here.
|
later without changing anything here.
|
||||||
|
|
||||||
|
The printed token is a secret in transit: `--print-config` output must go to the operator's
|
||||||
|
terminal and the config file, never into an installer log file or a support bundle.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. Version tracking, the bundle, and release orchestration
|
## 7. Version tracking, the bundle, and release orchestration
|
||||||
|
|||||||
@@ -23,7 +23,31 @@ Every route **except `GET /health`** requires the shared token from `sidecar.tom
|
|||||||
| REST | `X-Api-Key: <token>` |
|
| REST | `X-Api-Key: <token>` |
|
||||||
| WebSocket | `?token=<token>` in the connect URL (browsers can't set headers on a WS handshake) |
|
| WebSocket | `?token=<token>` in the connect URL (browsers can't set headers on a WS handshake) |
|
||||||
|
|
||||||
Missing or wrong token → **401** `{"error":"missing or invalid auth token"}`. The token is compared in constant time. It is generated automatically on first run (the sidecar logs it); rotate by editing `sidecar.toml` and restarting.
|
Missing or wrong token → **401** `{"error":"missing or invalid auth token"}`. The token is compared in constant time. It is generated automatically on first run; rotate by editing `sidecar.toml` and restarting.
|
||||||
|
|
||||||
|
To read it back afterwards, ask the sidecar rather than hunting through the startup log or the TOML:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uo-link-sidecar --print-config --config /etc/runicgateway/sidecar.toml
|
||||||
|
{
|
||||||
|
"component": "uo-link-sidecar",
|
||||||
|
"config_created": false,
|
||||||
|
"config_path": "/etc/runicgateway/sidecar.toml",
|
||||||
|
"protocol": 3,
|
||||||
|
"shard": { "bind": "127.0.0.1:7788" },
|
||||||
|
"store": { "path": "/var/lib/runicgateway/uo-link.db" },
|
||||||
|
"token_generated": false,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"web": {
|
||||||
|
"auth_required": true,
|
||||||
|
"auth_token": "c0f04ace…",
|
||||||
|
"bind": "127.0.0.1:8080",
|
||||||
|
"ws_path": "/ws"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That is the same set of values Admin → Shard asks for — base URL and WS URL are `web.bind` (substituting a reachable host if it is `0.0.0.0`) plus `web.ws_path`. The output **contains the token in clear text**, so treat it as a secret: it belongs in a terminal, not in a log or a CI artifact. `--print-config` also performs first-run setup, writing the config file and generating a token if there is none, and reports whether it did via `config_created` / `token_generated`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user