docs: sort the top-level directories into five sections by what the reader wants
Every docs directory moves under 1-getting-started, 2-handbook, 4-implementation or 5-project; the options reference (section 3) comes from the website and has no directory here. trust-boundary stays whole under the handbook for now. web-ui, swarm and tools split between handbook and implementation, and the command-line references move to 2-handbook/reference. Links, path references in code and nix, and the docs index follow the moves; no page content changes.
This commit is contained in:
parent
53ccbcb4b3
commit
6739bb903d
1 changed files with 736 additions and 701 deletions
|
|
@ -1,442 +0,0 @@
|
|||
# Swarm Matrix homeserver
|
||||
|
||||
Private Matrix homeserver (matrix-tuwunel — the conduwuit
|
||||
successor) wrapped in a nixos-container (`hive-matrix`), plus optional fluffychat-web
|
||||
client at `chat.<swarm-domain>/` (the `gatewayHost` vhost). A swarm runs one
|
||||
homeserver, on one host. Two namespaces configure it:
|
||||
|
||||
- `services.hyperhive.swarm.matrix.*` — what the homeserver **is**, as
|
||||
every hive sees it: `serverName`, `gatewayHost`.
|
||||
- `services.hyperhive.deploy.matrix.*` — what the host running it decides:
|
||||
`enable`, `gui.package`, `trustedServers`,
|
||||
`maxRequestSize`, `sso.clientSecretFile`.
|
||||
|
||||
Vhost routing lives in [`gateway.md`](../networking/gateway.md).
|
||||
|
||||
## Container shape
|
||||
|
||||
Same shape as [`gateway.md::hive-forge container shape`](../networking/gateway.md):
|
||||
|
||||
- Container name `hive-matrix` (not `h-*`) so c0re's lifecycle
|
||||
scanner ignores it; operator manages via the standard
|
||||
`nixos-container` CLI.
|
||||
- Keeps hive-matrix from fighting any `services.matrix-*` the
|
||||
operator already runs on the host — separate systemd namespace,
|
||||
separate state dir.
|
||||
- Container runs in a network namespace of its own
|
||||
(`privateNetwork = true`) on the hive bridge, and binds no TCP port. Agents
|
||||
reach the homeserver at `chat.<swarm-domain>` via the gateway, which
|
||||
dials its unix socket (see [Reach](#reach)).
|
||||
- Persistent state at
|
||||
`/var/lib/nixos-containers/hive-matrix/var/lib/matrix-tuwunel/`
|
||||
survives container restart / host reboot. To wipe, destroy the
|
||||
container.
|
||||
|
||||
## Identity vs API listener: `serverName` vs `gatewayHost`
|
||||
|
||||
Two distinct hostnames:
|
||||
|
||||
- **`serverName`** — matrix-spec `server_name`, embedded
|
||||
*irrevocably* in every `@user:<server_name>` and `!room:<server_name>`
|
||||
identifier minted on this homeserver. You can't change it later
|
||||
without abandoning every account and chat history. Defaults to the
|
||||
bare `services.hyperhive.swarm.domain`.
|
||||
- **`gatewayHost`** — the API listener hostname, where the gateway's
|
||||
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
|
||||
`chat.<services.hyperhive.swarm.domain>`. Set to `null` to skip the
|
||||
gateway vhost, which leaves nothing able to reach the homeserver.
|
||||
|
||||
Both default under the **swarm** domain, because a swarm runs one
|
||||
homeserver: tying its identity to a single hive's domain would make
|
||||
relocating the container between hives look like a different
|
||||
homeserver.
|
||||
|
||||
⚠️ **they're still not interchangeable, and the difference is the
|
||||
cost of changing one.** `gatewayHost` is a routing detail clients
|
||||
rediscover through `.well-known`, so it's safe to move on a running
|
||||
deployment. The controller's host serves that `.well-known` on the swarm
|
||||
domain, and a homeserver pinned to a hive domain gets it on that hive's
|
||||
vhost. The matrix id format bakes `serverName` into every user and
|
||||
room id, so adopting a new one does **not** rename the existing users and rooms —
|
||||
it strands them, because their ids still name a homeserver that no
|
||||
longer answers.
|
||||
|
||||
<details><summary>Pinning <code>serverName</code> on a homeserver with existing ids</summary>
|
||||
|
||||
`serverName` defaults to the bare `services.hyperhive.swarm.domain`. A
|
||||
homeserver that changes `serverName` after minting ids strands its
|
||||
existing users and rooms — keep the value it minted them under:
|
||||
|
||||
```nix
|
||||
services.hyperhive.swarm.matrix = {
|
||||
# whichever this deployment already uses
|
||||
serverName = config.services.hyperhive.domain;
|
||||
gatewayHost = "matrix.${config.services.hyperhive.domain}";
|
||||
};
|
||||
```
|
||||
|
||||
A rebuild on a host that already has a homeserver prints a
|
||||
`hive-matrix: WARNING — … serverName is unset` line when this is missing,
|
||||
naming the value it's about to default to. It never fails the rebuild, so
|
||||
pin the value before the homeserver mints any ids under the default.
|
||||
|
||||
A pinned hive domain answers `.well-known/matrix/*` itself, built from
|
||||
`gatewayHost`, on any host that runs that hive's gateway. It doesn't need
|
||||
the homeserver on the same host.
|
||||
|
||||
</details>
|
||||
|
||||
## Reach
|
||||
|
||||
tuwunel binds no TCP port. It listens on the unix socket
|
||||
`/run/hive-matrix/tuwunel.sock`, in a host directory (`root:nginx`,
|
||||
`0770`) bind-mounted into the container, and the gateway's
|
||||
`/_matrix/` location proxies to that socket. Clients, agents, hive-c0re
|
||||
and federation peers all reach the homeserver at `chat.<swarm-domain>`
|
||||
through the gateway; from outside the host that takes
|
||||
`gateway.openFirewall`.
|
||||
|
||||
hive-c0re doesn't talk to the homeserver: matrix is swarm-level, and
|
||||
the hive has no matrix API URL to set.
|
||||
|
||||
Federation peers find the homeserver through `.well-known/matrix/server`
|
||||
at the server name, which names `<gatewayHost>:443`
|
||||
([`gateway.md::Discovery flow`](../networking/gateway.md#discovery-flow-matrix)).
|
||||
Port 8448 isn't used.
|
||||
|
||||
Outbound, the container sits on the hive bridge like the CI runner: it
|
||||
leases an address from the gateway's DHCP, resolves through its dnsmasq,
|
||||
and leaves through the bridge's NAT. It reaches the secret store,
|
||||
authelia and federation peers by name.
|
||||
|
||||
## Provisioning flow (appservice)
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
Registration is closed. **The swarm's appservice** creates accounts: agents
|
||||
never see an appservice token, and an agent only ever receives its own
|
||||
`access_token`.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
The appservice has no URL (`url: null` in its registration), so the
|
||||
homeserver never calls out to it and there is no service to run. What the
|
||||
registration buys is an identity the homeserver recognises — which is why
|
||||
no secret has to be equal on both sides of the wire, and why account
|
||||
creation doesn't depend on registration being open to anyone who learns
|
||||
a token.
|
||||
|
||||
1. **`swarm-matrix-ctl appservice render`**, inside the `hive-matrix`
|
||||
container and before tuwunel starts, fetches the `as_token` and
|
||||
spec-required `hs_token` from the store and renders the registration to
|
||||
`/run/swarm-matrix-appservice/swarm.yaml` (mode `0600 root:root`) on the
|
||||
container's tmpfs, so no token sits on disk. It mints a pair only when
|
||||
the store answers and holds none. While the store doesn't answer, `render`
|
||||
fails and retries, and tuwunel stays down.
|
||||
2. **systemd `LoadCredential=`** copies the registration into
|
||||
`/run/credentials/tuwunel.service/swarm-appservice.yaml`, owned by
|
||||
tuwunel's dynamic user with mode `0400`, at service start. The source
|
||||
file stays `root:root 0600` — no `chown :tuwunel` / `chmod 0640` /
|
||||
GID-pin gymnastics required. Keeps `DynamicUser = true` +
|
||||
`PrivateUsers = true` intact.
|
||||
3. tuwunel's `appservice_dir` points at the credentials directory. It
|
||||
reads only `.yaml`/`.yml` entries from there, so the sibling
|
||||
credentials are invisible to it. The `.yaml` suffix on the credential
|
||||
id is what makes this work.
|
||||
4. **hive-c0re** holds no appservice token, creates no account and does
|
||||
not talk to matrix.
|
||||
5. **`swarm-controller` creates every account**: each agent's, with its
|
||||
token stored at `swarm/agents/<agent>/matrix/main` for the agent's
|
||||
`hive-matrix-daemon` to read as the agent itself, and each hive's
|
||||
sender account (below).
|
||||
|
||||
A homeserver whose `swarm-matrix-ctl` has no store identity
|
||||
(`services.hyperhive.deploy.matrix.ctlBaoClientCertFile`) loads no
|
||||
registration, and nothing creates accounts on it.
|
||||
|
||||
On the store's host `ctlBaoClientCertFile` / `ctlBaoClientKeyFile` default
|
||||
to the leaf `glue-bao-tls.nix` mints there. A homeserver host without the
|
||||
store gets no default, even when it holds the hive's own reader
|
||||
`deploy.bao.clientCertFile`: issue a leaf whose subject is
|
||||
`deploy.bao.matrixCtlCommonName`, place it, and name it in both options.
|
||||
Until you do, `swarm-bao-matrix-oidc` fails naming both.
|
||||
|
||||
### The swarm's appservice, and its admin sender
|
||||
|
||||
The registration's id is `swarm`, its sender `@swarm`, and its user
|
||||
namespace non-exclusive. It's the swarm's identity on the homeserver:
|
||||
`swarm-controller` creates every agent's account and every hive's sender
|
||||
account with it.
|
||||
|
||||
The swarm secret store holds the registration's tokens, at
|
||||
`swarm/controller/swarm-controller/matrix/appservice-registration`.
|
||||
`swarm-matrix-ctl appservice render` fetches them and renders the
|
||||
registration tuwunel loads as a credential onto the `hive-matrix`
|
||||
container's tmpfs, before tuwunel starts.
|
||||
A render that can't reach the store fails and retries, and tuwunel, which fails to start without the registration, retries until one has landed.
|
||||
`appservice sync` runs beside tuwunel on a timer. The store's tokens win:
|
||||
when they differ from the rendered copy, it rewrites it. It
|
||||
restarts tuwunel whenever the registration is newer than tuwunel's last
|
||||
start, so the next run retries one that failed after writing, and starts
|
||||
one that failed because `render` couldn't reach the store. A store that
|
||||
holds none takes the tokens an earlier version kept in
|
||||
`/var/lib/swarm-matrix-appservice` with a create-only write, so a running
|
||||
swarm keeps its value; the files stay, unused. A host with neither files nor stored tokens mints a pair. `sync` then writes the `as_token`
|
||||
to `swarm/controller/swarm-controller/matrix/appservice-token`. No hive's
|
||||
policy reaches either path: only matrix-ctl (which holds `create` and
|
||||
`read` on the registration, and writes the token) and `swarm-controller`
|
||||
(which reads only the token) hold a grant.
|
||||
|
||||
`@swarm` is the **one** account the homeserver promotes to admin at boot
|
||||
(`admin_execute`). Only those two principals read its token, which is
|
||||
the difference from the hive sender below: every hive reads its own
|
||||
sender's token. `swarm` is a reserved name, so nothing can create an agent
|
||||
as this account.
|
||||
|
||||
`swarm-controller` mints each agent's account with the device id
|
||||
`hyperhive-<agent>`, and a login on that device replaces its token. The controller therefore reads
|
||||
the stored token back with `whoami` every five minutes and re-mints only
|
||||
when it's missing, unknown to the homeserver, or someone else's. An
|
||||
agent's daemon picks a re-minted token up on its next start, and a timer
|
||||
restarts it while it's down.
|
||||
|
||||
### Operators are homeserver admins
|
||||
|
||||
Every member of authelia's `admins` group who has a matrix account is a
|
||||
homeserver admin. `swarm-controller` reads the group through
|
||||
`swarm-authelia-bridge` at start and every five minutes after, and for
|
||||
each member not yet in `#admins:<server_name>` it sends
|
||||
`!admin users make-user-admin @<name>:<server_name>` there as `@swarm`.
|
||||
`<name>` is the authelia username lowercased, the account an SSO login
|
||||
lands on.
|
||||
|
||||
- **Leaving `admins` revokes what the sync granted.** The controller
|
||||
records each account it sends a grant for in
|
||||
`/var/lib/swarm-controller/operator-matrix-admins.json`. When one of
|
||||
them is no longer in `admins` but still in `#admins`, it sends
|
||||
`!admin users force-leave-room @<name>:<server_name> <admin room id>`
|
||||
there as `@swarm`; leaving that room is what ends homeserver admin.
|
||||
- **Admins the sync didn't make keep admin.** An admin missing from that
|
||||
file, `@swarm`, the server user `@conduit:<server_name>`, every agent's
|
||||
account, and every user id in
|
||||
`services.hyperhive.deploy.swarm-controller.matrixAdminSync.ignoreUsers` are never
|
||||
revoked.
|
||||
- **The last admin stays.** A revoke that would leave no admin in
|
||||
`#admins` besides `@swarm` and the server user isn't sent; the
|
||||
controller logs a warning on each pass instead.
|
||||
- **No account, no grant.** A member who has never logged in to matrix
|
||||
has no account to promote. The first pass after their first SSO login
|
||||
promotes them.
|
||||
- **One command per account per controller run.** A member still missing
|
||||
from `#admins` after the controller sends its grant gets a
|
||||
`make-user-admin … failed to take` warning in the controller log on
|
||||
each later pass, and no second command until the controller restarts;
|
||||
a revoke that leaves its account in `#admins` gets
|
||||
`force-leave-room … failed to take` the same way.
|
||||
- Runs only on a controller with both
|
||||
`services.hyperhive.deploy.swarm-controller.authBridgeUrl` and
|
||||
`services.hyperhive.deploy.swarm-controller.matrixHomeserverUrl` set.
|
||||
|
||||
### A hive's sender account, and why it isn't an admin
|
||||
|
||||
`@hive-<hive>:<server_name>` is the account a hive acts as.
|
||||
`swarm-controller` creates it with the swarm's appservice token, for every
|
||||
hive in its directory. It's an **ordinary account**: nothing promotes it;
|
||||
the homeserver's `admin_execute` promotes only `@swarm`.
|
||||
|
||||
**One account per hive.** The localpart carries the hive's name, so a swarm
|
||||
whose hives share a homeserver gives each of them its own identity: the
|
||||
homeserver attributes an action to the hive that took it, and revoking one
|
||||
hive's standing leaves the others alone.
|
||||
|
||||
Its access token is the **sender token**. `swarm-controller`, its only
|
||||
minter, mints it for every hive with the swarm's appservice token and
|
||||
publishes it to `swarm/hives/<hive>/matrix/sender-token`, inside the hive's own
|
||||
read grant (`swarm/hives/<hive>/*`). Nothing on the hive uses it any more:
|
||||
hive-c0re used to present it for its Space and invite calls and no longer
|
||||
makes any. The name says what it authenticates as — an account the
|
||||
swarm's appservice creates — rather than any privilege level, because it
|
||||
carries none.
|
||||
|
||||
Promoting a user to homeserver admin and resetting a password both need an
|
||||
admin **sender**: `!admin …` messages into `#admins:<server_name>`, and
|
||||
tuwunel only treats a message as a command when its sender is already an
|
||||
admin. Of the swarm's own accounts only `@swarm` is a homeserver admin
|
||||
(operators are too, see
|
||||
[Operators are homeserver admins](#operators-are-homeserver-admins)), so
|
||||
both are swarm-level operations.
|
||||
|
||||
<details><summary>Appservice identity</summary>
|
||||
|
||||
`swarm-matrix-ctl appservice render` renders the registration from the
|
||||
store's copy of the appservice tokens before tuwunel starts, so every
|
||||
boot has both halves. With the store down or sealed, tuwunel waits for it.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
- **Access tokens live on the device that minted them.**
|
||||
`login_with_password` stays on, so the password fallback works
|
||||
too.
|
||||
- **The homeserver's `admin_execute` promotes only `@swarm` at boot**
|
||||
(above); a hive's sender account is never promoted.
|
||||
- **The value that matters lives at
|
||||
`swarm/controller/swarm-controller/matrix/appservice-token`.**
|
||||
`appservice sync` writes it there whenever the store's differs from the
|
||||
registration's `as_token`, and `swarm-controller` reads it from there.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
</details>
|
||||
|
||||
Initial rollout settings:
|
||||
|
||||
- `allow_federation = true` at the protocol level so swarms can be
|
||||
wired up later by extending `trustedServers` without a homeserver
|
||||
restart. `trusted_servers = []` keeps it effectively closed
|
||||
until you list peers.
|
||||
- `allow_registration = false`. tuwunel checks this flag only for
|
||||
requests that arrive **without** an appservice token, so the swarm's
|
||||
appservice still creates accounts and tuwunel refuses everyone else. With no
|
||||
registration token configured, `allow_registration = true` makes
|
||||
tuwunel refuse to start unless
|
||||
`yes_i_am_very_very_sure_…_open_registration_…` is also set.
|
||||
- `allow_encryption` — server-side E2EE switch, sourced from
|
||||
`services.hyperhive.deploy.matrix.allowEncryption` (**default `false`**, opt-in).
|
||||
Off by default because on the swarm's own homeserver the operator
|
||||
already controls the transport; turn it on for encrypted rooms on
|
||||
external / federated homeservers or to keep contents opaque to the
|
||||
homeserver admin. **The agent matrix client always supports decryption
|
||||
regardless of this flag** — it uses the `e2e-encryption` feature of
|
||||
`matrix-sdk` so it can read encrypted rooms it's invited to even when
|
||||
this homeserver doesn't permit room encryption. matrix-sdk stores
|
||||
crypto keys in the per-agent sqlite store under the state dir; they persist across
|
||||
restarts. `read_room` decrypts via
|
||||
`room.messages()` — UTD events surface as `event_type =
|
||||
"m.room.encrypted"` with `body = "[unable to decrypt]"`.
|
||||
The daemon bootstraps cross-signing on the bearer token alone, which
|
||||
works where the homeserver skips re-auth for a first key upload
|
||||
(MSC3967, as tuwunel does).
|
||||
- **Key backup.** `swarm-controller` mints a random recovery passphrase
|
||||
for every matrix account an agent has, at
|
||||
`swarm/agents/<agent>/matrix-recovery/<account>` in the swarm secret
|
||||
store, and never replaces it. When `hive-matrix-daemon` brings an
|
||||
account up it reads that passphrase: with no backup on the account's
|
||||
homeserver it turns on server-side key backup and secret storage
|
||||
(log line `matrix recovery enabled`), unless the account already has
|
||||
secret storage, which it never replaces (a `WARN` instead). With a
|
||||
backup there it recovers from it (`recovered from the server-side key backup`), which downloads the
|
||||
room keys and signs the new device with the account's existing
|
||||
cross-signing identity. A crypto store lost to `--purge` or to a
|
||||
primary-token reset therefore comes back with its history. With no passphrase
|
||||
stored yet the account comes up without backup and picks it up on the
|
||||
next daemon start. A failure is a `WARN` and never stops the daemon.
|
||||
|
||||
## Retired hive-side matrix code
|
||||
|
||||
Earlier versions of hive-c0re created a private "hive" Matrix Space and a
|
||||
`hive-chat` room, invited every agent to both, kept the hive's sender token
|
||||
on disk and offered `hivectl matrix invite`. Matrix is swarm-level, so
|
||||
hive-c0re dropped all of it and no longer talks to the homeserver. The Space, the
|
||||
room and the files `/var/lib/hyperhive/matrix/{access-token,space-room-id,chat-room-id}`
|
||||
are harmless leftovers, so delete them by hand if you like.
|
||||
|
||||
## Configuration tuning
|
||||
|
||||
```nix
|
||||
services.hyperhive.deploy.matrix = {
|
||||
trustedServers = [ "matrix.org" "example.com" ]; # default: []
|
||||
maxRequestSize = 20000000; # default: 20 MB
|
||||
};
|
||||
```
|
||||
|
||||
**`trustedServers`** (default `[]`) — list of peer homeserver names
|
||||
whose signing keys tuwunel fetches and trusts. tuwunel enables
|
||||
federation at the protocol level from first boot (`allow_federation =
|
||||
true`) but trusts no remote homeserver until you list it here. For a closed
|
||||
single-hive deployment the default empty list is correct — add peer
|
||||
hive domains here when connecting hives into a swarm (see
|
||||
[`docs/swarm/`](../swarm/README.md)).
|
||||
|
||||
**`maxRequestSize`** (default `20_000_000` bytes = 20 MB) — maximum
|
||||
size of a single matrix client request body. Matches the matrix-spec
|
||||
recommendation for media uploads and the upstream tuwunel default.
|
||||
Raise for deployments that need large file transfers; lower for
|
||||
resource-constrained hosts where a 20 MB request is unexpectedly large.
|
||||
|
||||
## Assertion rationale
|
||||
|
||||
`config.assertions` in this module fail eval early rather than ship
|
||||
surprising behaviour:
|
||||
|
||||
- **`services.hyperhive.swarm.matrix.gatewayHost != ""`** — same footgun as `forge.domain`:
|
||||
empty string renders `.<hive>`-shaped garbage in both nginx
|
||||
`server_name` (treated as wildcard catch-all, surprising) and
|
||||
`/etc/hosts` (malformed entry). `null` is the right opt-out shape;
|
||||
a config assertion rejects empty string explicitly.
|
||||
SSO is unconditional, so the three below are requirements of running a
|
||||
homeserver at all rather than of a setting:
|
||||
|
||||
- **Keep `deploy.matrix.sso.clientSecretFile` set** — it defaults to
|
||||
`/run/hive-matrix-oidc/<client id>.secret` wherever the homeserver runs,
|
||||
and `swarm-bao-matrix-oidc` writes the secret there from the swarm secret
|
||||
store under matrix-ctl's leaf. The path is on the host's tmpfs, so every
|
||||
reboot fetches the secret again and it never reaches persistent disk. After
|
||||
a reboot the homeserver doesn't start until the store answers; the
|
||||
store-retry timer then starts it with no manual step. A path
|
||||
outside `/run/hive-matrix-oidc/`, or a null, fails at eval, not at boot:
|
||||
tuwunel reads its identity providers from the config file, so a half-configured
|
||||
one can stop the homeserver from starting outright rather than merely
|
||||
hiding a login button.
|
||||
- **Set `swarm.authelia.url`** — without a provider URL there
|
||||
is nothing to discover against.
|
||||
- **Set `swarm.matrix.gatewayHost != null`** — the SSO callback URL is
|
||||
format-locked to `<homeserver>/_matrix/client/unstable/login/sso/callback/<client_id>`,
|
||||
and the identity provider needs a public name to redirect the
|
||||
browser to.
|
||||
|
||||
`server_name`'s own bogus-value guard lives in `hive-network.nix`
|
||||
(`services.hyperhive.domain != null`), not here — see
|
||||
[`docs/networking/network.md`](../networking/network.md).
|
||||
|
||||
## fluffychat-web build fixes
|
||||
|
||||
`pkgs.fluffychat-web` ships from `flutter341.buildFlutterApplication`,
|
||||
which has two upstream gaps for fluffychat's web target:
|
||||
|
||||
- The dart web-worker entry point (`web/native_executor.dart`) isn't
|
||||
compiled — `buildFlutterApplication` only runs `flutter build web`
|
||||
on the main entry.
|
||||
- `native_imaging`'s C source isn't built — emscripten isn't a
|
||||
flutter-builder native build input.
|
||||
|
||||
Both fixed in `nix/host-modules/hive-matrix.nix` via two derivations:
|
||||
|
||||
- **`fluffychat-web-imaging`** builds `Imaging.{js,wasm}` from the
|
||||
`native_imaging` C source via `pkgs.emscripten`. Source comes
|
||||
from `pkgs.fluffychat-web.passthru.pubspecLock.dependencySources.native_imaging`
|
||||
— already in the build closure of the flutter app, so no parallel
|
||||
hash pin and version autosyncs with nixpkgs bumps. Build closure
|
||||
is ~3.6 GiB (emscripten LLVM); runtime closure is just the two
|
||||
output files. `dontConfigure = true` because cmake runs inside
|
||||
`js/Makefile` via `emcmake cmake`, not at the package root. The
|
||||
build script needs `HOME` + `EM_CACHE` writable for emscripten's
|
||||
on-demand sysroot build (libc, libc++ → wasm).
|
||||
- **`fluffychat-web-fixed`** is `pkgs.fluffychat-web` plus a
|
||||
`postInstall` patch that (a) compiles `web/native_executor.dart`
|
||||
via `dart compile js` (dart from the flutter341 closure, no
|
||||
incremental cost) and (b) installs `fluffychat-web-imaging`'s
|
||||
outputs into `$out`.
|
||||
|
||||
Two subtle details worth knowing before touching either derivation:
|
||||
|
||||
- **`make -C js`** instead of `cd js; make` — keeps the build-phase
|
||||
pwd at the source root so `installPhase` doesn't have to know
|
||||
about the cd. Robust against future reorders / `dontBuild`.
|
||||
- **`web/native_executor.dart`** as a build-CWD-relative path,
|
||||
*not* `$src/web/...` — `dart`'s `package_config.json` walk-up
|
||||
needs to hit `buildFlutterApplication`'s pub-get output
|
||||
(`.dart_tool/` in the build CWD). Walking up from a read-only
|
||||
`$src/` store path finds no `.dart_tool/` and errors with
|
||||
"Couldn't resolve the package 'matrix'."
|
||||
|
||||
Drop both derivations when nixpkgs's flutter builder grows worker
|
||||
+ emcc support upstream.
|
||||
|
||||
Mount point is `chat.<swarm-domain>/` (the `gatewayHost` vhost);
|
||||
upstream `--base-href "/"` is correct at sub-domain root, no override.
|
||||
Loading…
Reference in a new issue