Ng eventually #1

Open
Sylvain wants to merge 110 commits from ng-eventually into main
6 changed files with 39 additions and 19 deletions
Showing only changes of commit c3d64555d9 - Show all commits
-7
View File
@@ -1,7 +0,0 @@
# Doc-debt — tech-stack
> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file.
> One block = one "big change": `why` + `files` + `verify` (leaves to review).
## Raw markers (consolidate into blocks, then delete)
- TOUCHED src/index.ts @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1)
+1 -1
View File
@@ -3,7 +3,7 @@ type: _overview
summary: Stack and tooling — Bun-first (runtime, bundler, native APIs), build pipeline, and the project's commands
triggers:
keywords: [bun, bunx, build, bundler, vite, webpack, jest, npm, storybook, "bun.serve", hmr, tailwind, package.json]
paths: ["build.ts", "package.json", "bunfig.toml", "tsconfig.json", "src/index.ts", "src/index.html", ".storybook/**", "scripts/**"]
paths: ["build.ts", "package.json", "pnpm-lock.yaml", "Dockerfile", ".env.example", "bunfig.toml", "tsconfig.json", "src/index.ts", "src/index.html", ".storybook/**", "scripts/**"]
---
# Tech stack
@@ -1,11 +1,13 @@
---
type: knowledge
summary: Deployment — multi-stage Bun Alpine Dockerfile; install through pnpm (git+node inside the image) but bun at runtime; runs `bun run start` from src/ (not dist/), EXPOSE 3000, env PORT/NODE_ENV; no CI/CD committed; dev goes through the portless wrapper
last_checked: 2026-07-14
summary: Deployment — multi-stage Bun Alpine Dockerfile; install through pnpm (git+node inside the image) but bun at runtime; runs `bun run start` from src/ (not dist/), EXPOSE 3000; the data-layer git dependency must be pinned to a tag/commit and match `contracts.yaml`'s ref; the shared wallet reaches the container through env vars, not a mount, because it isn't a secret; no CI/CD committed; dev goes through the portless wrapper
last_checked: 2026-08-17
---
# Deployment & infra
Nothing has actually been deployed with this shape yet — this leaf states what a deployment needs to line up, verified against the code and manifests, not a procedure that has been run end to end.
## Dockerfile
A `Dockerfile` exists (multi-stage Bun Alpine). **Installation goes through pnpm, but runtime/build/test stay on bun** (see [[knowledge_stack-and-commands]]):
@@ -14,16 +16,41 @@ A `Dockerfile` exists (multi-stage Bun Alpine). **Installation goes through pnpm
**`bun` peer pitfall**: `bun-plugin-tailwind` declares `bun` as a peerDependency → pnpm materializes the npm `bun` package and **creates a `node_modules/.bin/bun` shim** that shadows the `bun` from the PATH under `bun run`/`pnpm run`. Its postinstall is ignored by default → broken shim → `bun run start` fails. Fixed by approving the build: `pnpm.onlyBuiltDependencies: ["bun"]` in `package.json` (the postinstall then downloads the real binary). Without that, the whole pnpm migration breaks startup.
**`tailwindcss` is a devDependency the server needs at serve time, not only at build time.** `bunfig.toml`'s `[serve.static] plugins = ["bun-plugin-tailwind"]` applies to `Bun.serve`'s HTML-import serving — the path both `bun run dev` and `bun run start` use ([[knowledge_build-pipeline]]) — not only to `bun run build.ts`. The install stage must therefore keep installing devDependencies: no `--prod`, and `NODE_ENV` stays unset until the `release` stage, after `pnpm install --frozen-lockfile` has already run. Moving `ENV NODE_ENV=production` earlier, or adding `--prod` to the install, would drop `tailwindcss` and break every serve, dev included.
**Production runs the sources, and this is the normal path, not a quirk**: `start` = `NODE_ENV=production bun src/index.ts` → the container **runs the TypeScript directly** (Bun transpiles on the fly). `bun run build` (→ `dist/`) is on **no** path at all — nothing serves that directory, in this container or anywhere else; serving it would mean changing the entrypoint. Consequence for the code: in this deployment `NODE_ENV=production` says *how* the sources run, never *that they were bundled* — [[knowledge_build-pipeline]].
## The data-layer git dependency must stay pinned, and the pin must be checkable
`package.json` resolves `@ng-eventually/polyfill` from `git+https://…/ng-eventually.git#<ref>&path:/packages/polyfill` — the `path:` selector is what lets a subdirectory of the provider's repo be installed as the package. Two things follow, ahead of any real deployment:
- **`<ref>` must name a tag or a commit, never a branch.** A branch moves: the image was built against whatever commit the branch pointed to at build time, and the branch head can advance afterwards without the image changing — so "the same deployment" silently starts drifting from what it was actually built against. The tag-naming convention itself is the provider's call and is not settled yet; the requirement is only that the ref be immutable.
- **The same `<ref>` should also be the `ref:` of the `polyfill-surface` entry in `.project/contracts.yaml`.** That manifest pins the version of [[contract_polyfill-surface]] the app is coded against; when it names the same ref as `package.json`'s specifier, the contract the app was written for and the package actually installed name the same state, and a difference between the two becomes visible instead of silent. Both now name the **same commit**, which is the state a deployment can ship on. A tag is expected to replace that commit once the provider settles a naming convention — a one-line change in each of the two files, with the invariant unchanged: whatever the ref is, the two must agree.
**`pnpm install --frozen-lockfile` (the Dockerfile's install step) never regenerates — it only verifies.** `pnpm-lock.yaml` must already reproduce `package.json` exactly, so any change to the git specifier (ref, path, or package name) needs `pnpm install` run and the regenerated lockfile committed *before* the image can build; skipping that step fails the build outright, not silently. This has bitten once: the lockfile still named the old package and path after the dependency was renamed, so `--frozen-lockfile` refused and the image could not build until it was regenerated.
## CI/CD
**No** pipeline is committed (`.github/workflows/` absent, no Coolify config in the repo). A knowingly accepted blind spot. To host the Bun app, the `coolify-hosting` skill applies.
Also untested: whether a **deployed origin** can be embedded in the hosted broker's iframe at all — the SDK's identity flow runs the app inside that iframe (see [[contract_polyfill-surface]]'s `ensureIdentity()` barrier). [[caveat_firefox-lna-blocks-broker-iframe]] records the one failure mode known here, and it is a **local-dev-origin** one (`127.0.0.1` blocked by Firefox LNA); no scenario in this repo exercises a real deployed origin against the broker's embedding policy.
## Environment variables
- `PORT` (default 3000), `NODE_ENV` (enables/disables HMR and the dev auto-seed — see concept `data-layer`).
- No `.env*` is committed (`.env` is gitignored). No secret management in the repo.
- No `.env*` is committed (`.env` is gitignored).
### The shared wallet: config, not a secret, not a mount
[[contract_polyfill-surface]] requires the app to serve a wallet file (`.ngw`) and pass its URL and password to `configure` as `sharedWallet: { fileUrl, password }`. `*.ngw` is gitignored and no deployment mounts one, so `src/index.ts` serves it from environment variables, read fresh on every request:
- `FESTIPOD_SHARED_WALLET_PASSWORD` — the password, always read this way (dev, tests, and deployments alike).
- `FESTIPOD_SHARED_WALLET_FILE` — a filesystem path to the `.ngw` file. The form local dev and the test harness use: the file sits on the machine's disk.
- `FESTIPOD_SHARED_WALLET_FILE_BASE64` — the file's bytes, base64-encoded. The form a deployment uses instead, since nothing mounts a `.ngw` into the container.
**Precedence is one-directional and does not fall through.** `FESTIPOD_SHARED_WALLET_FILE` wins whenever it is set, *even if the path turns out unreadable* — an unreadable path answers 404, it does **not** fall back to the base64 form. A deployment must set exactly one of the two; leaving a leftover `FESTIPOD_SHARED_WALLET_FILE` pointing nowhere in a deployment environment silently 404s instead of serving the base64 value that was actually intended. A malformed base64 value answers 500 naming the variable — never a 404, which would be indistinguishable from "not configured at all".
**Neither the password nor the wallet file is a secret**, and that is deliberate, not an oversight: the contract has the app hand both to every user who opens it — that is how a first-time device without its own wallet onboards. Provisioning them as protected/mounted storage would guard something the app already gives away by design; they travel as plain configuration instead, and a new host needs only its environment variables, nothing to mount.
## Dev
+1 -1
View File
@@ -19,4 +19,4 @@ consume:
# The contract is published from the branch that carries it while that branch is still
# in flight; it moves to `main` once the provider lands it there. Flip this line then,
# and re-pull — the stamp records which commit the local copy actually came from.
ref: caps-p1a-and-virtual-user-boundary
ref: a8d53010c227462cc9317e9be499c2100ca8d533
+1 -1
View File
@@ -22,7 +22,7 @@
"build-storybook": "storybook build"
},
"dependencies": {
"@ng-eventually/polyfill": "git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#main&path:/packages/polyfill",
"@ng-eventually/polyfill": "git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#a8d53010c227462cc9317e9be499c2100ca8d533&path:/packages/polyfill",
"@ng-org/alien-deepsignals": "0.1.2-alpha.11",
"@ng-org/orm": "0.1.2-alpha.18",
"@ng-org/shex-orm": "0.1.2-alpha.8",
+6 -6
View File
@@ -8,9 +8,9 @@ importers:
.:
dependencies:
'@ng-eventually/client':
specifier: git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#main&path:/packages/client
version: git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#1f0bae461e461c9fddd7215f972418acb2b4a989&path:/packages/client(@ng-org/alien-deepsignals@0.1.2-alpha.11(react@19.2.7))(@ng-org/orm@0.1.2-alpha.18(react@19.2.7))(@ng-org/shex-orm@0.1.2-alpha.8(typescript@6.0.3))(@ng-org/web@0.1.2-alpha.13)
'@ng-eventually/polyfill':
specifier: git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#a8d53010c227462cc9317e9be499c2100ca8d533&path:/packages/polyfill
version: git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#a8d53010c227462cc9317e9be499c2100ca8d533&path:/packages/polyfill(@ng-org/alien-deepsignals@0.1.2-alpha.11(react@19.2.7))(@ng-org/orm@0.1.2-alpha.18(react@19.2.7))(@ng-org/shex-orm@0.1.2-alpha.8(typescript@6.0.3))(@ng-org/web@0.1.2-alpha.13)
'@ng-org/alien-deepsignals':
specifier: 0.1.2-alpha.11
version: 0.1.2-alpha.11(react@19.2.7)
@@ -488,8 +488,8 @@ packages:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
'@ng-eventually/client@git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#1f0bae461e461c9fddd7215f972418acb2b4a989&path:/packages/client':
resolution: {commit: 1f0bae461e461c9fddd7215f972418acb2b4a989, path: /packages/client, repo: https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git, type: git}
'@ng-eventually/polyfill@git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#a8d53010c227462cc9317e9be499c2100ca8d533&path:/packages/polyfill':
resolution: {commit: a8d53010c227462cc9317e9be499c2100ca8d533, path: /packages/polyfill, repo: https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git, type: git}
version: 0.0.0
peerDependencies:
'@ng-org/alien-deepsignals': '*'
@@ -3727,7 +3727,7 @@ snapshots:
'@tybys/wasm-util': 0.10.3
optional: true
'@ng-eventually/client@git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#1f0bae461e461c9fddd7215f972418acb2b4a989&path:/packages/client(@ng-org/alien-deepsignals@0.1.2-alpha.11(react@19.2.7))(@ng-org/orm@0.1.2-alpha.18(react@19.2.7))(@ng-org/shex-orm@0.1.2-alpha.8(typescript@6.0.3))(@ng-org/web@0.1.2-alpha.13)':
'@ng-eventually/polyfill@git+https://gitea.reconnexion.apps.gueraud.net/Reconnexion/ng-eventually.git#a8d53010c227462cc9317e9be499c2100ca8d533&path:/packages/polyfill(@ng-org/alien-deepsignals@0.1.2-alpha.11(react@19.2.7))(@ng-org/orm@0.1.2-alpha.18(react@19.2.7))(@ng-org/shex-orm@0.1.2-alpha.8(typescript@6.0.3))(@ng-org/web@0.1.2-alpha.13)':
optionalDependencies:
'@ng-org/alien-deepsignals': 0.1.2-alpha.11(react@19.2.7)
'@ng-org/orm': 0.1.2-alpha.18(react@19.2.7)