clone

a self-hosted sync node for lofi apps: jazz sync over iroh, ticket-gated, with no static ip or cloud needed

feat: container image — Dockerfile, compose reference, CI smoke, GHCR publish

Dami committed f2438b0
linux/amd64 image: deno-compile build stage (committed iroh prebuilts,
no Rust toolchain), debian-slim glibc runtime, non-root user, /data
volume, gate HEALTHCHECK. ci.yml gains a container smoke that requires
mesh: up inside the container; publish.yml builds, smokes, and pushes
ghcr.io/felinestatemachine/lofi-node on each tag.

linux-arm64 is dropped from the release binary matrix: jazz-napi ships
no linux-arm64-gnu platform package, so that binary could not boot at
all (verified against the v0.2.0 asset). It returns when upstream
publishes the package; until then the gap is documented instead of
shipped.
commit
f2438b0083255f7c020cae7d519d59adfaf48567
parent
985da89b6c42c5d10c4fc1c3ffe88648f4ea7457
author
Dami <dami.etoile@gmail.com>
7 files changed, +193 -8
A.dockerignore17+++++++++
M.github/workflows/ci.yml27+++++++++++++++
M.github/workflows/publish.yml51+++++++++++++++++++++++++++--
MCONTRIBUTING.md8+++--
ADockerfile54++++++++++++++++++++++++++++++
MREADME.md19+++++++++-
Acompose.yaml25++++++++++++++
A .dockerignore +17 -0
M .github/workflows/ci.yml +27 -0
M .github/workflows/publish.yml +48 -3
@@ -56,18 +56,19 @@ jobs:
5656 - name: Publish to JSR5757 run: deno publish5858 59- - name: Compile release binaries (3 targets)59+ # No linux-arm64 binary: jazz-napi ships no linux-arm64-gnu platform60+ # package, so that target cannot boot the Jazz server. Restore it when61+ # upstream publishes the package.62+ - name: Compile release binaries6063 run: |6164 deno task compile:darwin-arm646265 deno task compile:linux-x6463- deno task compile:linux-arm646466 6567 - name: Stage release assets6668 run: |6769 mkdir -p dist/assets6870 cp dist/lofi-node-aarch64-apple-darwin dist/assets/6971 cp dist/lofi-node-x86_64-unknown-linux-gnu dist/assets/70- cp dist/lofi-node-aarch64-unknown-linux-gnu dist/assets/7172 # Raw addon artifacts under the names artifacts.ts pins.7273 cp native/iroh-js/prebuilt/aarch64-apple-darwin/libnumber0_iroh.dylib \7374 dist/assets/libnumber0_iroh-aarch64-apple-darwin.dylib
@@ -99,3 +100,47 @@ jobs:
99100 deno run --minimum-dependency-age=0 -A "jsr:@nzip/lofi-node@$version/cli" | head -3100101 deno eval --minimum-dependency-age=0 "import { looksLikeAppTicket } from 'jsr:@nzip/lofi-node@$version'; if (!looksLikeAppTicket('lofisync1.abcdef')) throw new Error('lib smoke failed'); console.log('library import ok')"101102 deno x --minimum-dependency-age=0 -A "jsr:@nzip/lofi-node@$version/cli" && echo "deno x subpath form works" || echo "::warning::deno x probe failed (expected while the fresh version is inside the minimum-dependency-age window; deno x does not accept the override yet)"103+104+ docker:105+ needs: publish106+ runs-on: ubuntu-latest107+ permissions:108+ contents: read109+ packages: write # GHCR push110+ steps:111+ - uses: actions/checkout@v4112+113+ - name: Build image (linux/amd64)114+ run: |115+ version="${GITHUB_REF_NAME#v}"116+ image="ghcr.io/felinestatemachine/lofi-node"117+ docker build -t "$image:$version" -t "$image:latest" .118+119+ - name: Image smoke — init, start, health, mesh up120+ run: |121+ version="${GITHUB_REF_NAME#v}"122+ image="ghcr.io/felinestatemachine/lofi-node"123+ docker volume create smoke124+ docker run --rm -v smoke:/data "$image:$version" \125+ init --dir /data --port 4802 --public-url http://127.0.0.1:4802126+ docker run -d --name smoke -p 4802:4802 -v smoke:/data "$image:$version"127+ for i in $(seq 1 30); do128+ curl -fsS http://127.0.0.1:4802/health >/dev/null 2>&1 && break129+ sleep 2130+ done131+ curl -fsS http://127.0.0.1:4802/health132+ docker logs smoke 2>&1 | grep -q "mesh: up"133+ docker rm -f smoke134+135+ - uses: docker/login-action@v3136+ with:137+ registry: ghcr.io138+ username: ${{ github.actor }}139+ password: ${{ secrets.GITHUB_TOKEN }}140+141+ - name: Push image142+ run: |143+ version="${GITHUB_REF_NAME#v}"144+ image="ghcr.io/felinestatemachine/lofi-node"145+ docker push "$image:$version"146+ docker push "$image:latest"
M CONTRIBUTING.md +5 -3
A Dockerfile +54 -0
@@ -0,0 +1,54 @@
1+# Container image for lofi-node. The build stage compiles the self-contained2+# binary on the target platform (the committed prebuilt iroh addons make this3+# possible without a Rust toolchain); the runtime stage is a minimal glibc4+# base. The addons are gnu builds, so the base must be glibc — do not switch5+# to musl/alpine.6+#7+# linux/amd64 only: jazz-napi publishes no linux-arm64-gnu binary, so an8+# arm64 image cannot boot the Jazz server. The guard below fails the build9+# rather than producing an image that dies at runtime. arm64 hosts can run10+# the amd64 image under emulation (Docker Desktop Rosetta / qemu).11+#12+# Build: docker build -t lofi-node .13+# Run: see compose.yaml and https://lofi.host/node/docs/beyond-the-lan14+15+FROM denoland/deno:latest AS build16+ARG TARGETARCH17+RUN if [ "$TARGETARCH" != "amd64" ]; then \18+ echo "lofi-node images are linux/amd64 only: jazz-napi ships no" \19+ "linux-arm64-gnu binary (upstream gap). Run the amd64 image" \20+ "under emulation instead." >&2; \21+ exit 1; \22+ fi23+WORKDIR /src24+COPY deno.json deno.lock cli.ts mod.ts ./25+COPY src ./src26+COPY testing ./testing27+COPY native/iroh-js/prebuilt ./native/iroh-js/prebuilt28+# Materialize node_modules (jazz-napi's platform package) before compiling;29+# deno compile embeds it into the binary's vfs.30+RUN deno install31+RUN deno task compile:linux-x64 \32+ && mv dist/lofi-node-x86_64-unknown-linux-gnu dist/lofi-node33+34+FROM debian:bookworm-slim35+LABEL org.opencontainers.image.source="https://github.com/FelineStateMachine/lofi-node" \36+ org.opencontainers.image.description="Self-hostable sync node for lofi apps" \37+ org.opencontainers.image.licenses="MIT"38+# ca-certificates: TLS to iroh relays and upstream URLs. curl: HEALTHCHECK.39+RUN apt-get update \40+ && apt-get install -y --no-install-recommends ca-certificates curl \41+ && rm -rf /var/lib/apt/lists/* \42+ && useradd --uid 1000 --create-home lofi \43+ && mkdir /data && chown lofi:lofi /data44+COPY --from=build /src/dist/lofi-node /usr/local/bin/lofi-node45+USER lofi46+VOLUME /data47+# The documented container port convention; init with --port 4802 (see docs).48+EXPOSE 480249+# Probes the gate; unauthenticated in both access modes. Override the port in50+# compose if the node was initialized with a different one.51+HEALTHCHECK --interval=30s --timeout=5s --start-period=30s \52+ CMD curl -fsS http://127.0.0.1:4802/health || exit 153+ENTRYPOINT ["lofi-node"]54+CMD ["start", "--dir", "/data"]
M README.md +17 -2
A compose.yaml +25 -0