Compare commits

..
Author SHA1 Message Date
ssmithxandClaude Sonnet 5 bb0193932f Package Conduit Market as an Archipelago app
Pure client-side Nostr+Lightning marketplace (apps/market from
Conduit-BTC/conduit-mono) built into a static nginx image. Public/private
is a genuine runtime toggle, not two separate builds: Vite bakes VITE_*
relay config into the bundle at build time, so config.ts is patched
(apply-runtime-override.py) to merge in a window.__CONDUIT_RUNTIME_ENV__
override written by docker-entrypoint.sh from CONDUIT_MODE /
CONDUIT_PRIVATE_RELAY_URLS at container start -- flipping mode only needs
a restart, not a rebuild. Verified end-to-end: build succeeds, the
override survives bundling/minification, and public/private/missing-relay
all produce the correct runtime-env.js (private with no relay URL refuses
to start rather than silently falling back to the public network).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 23:21:27 +00:00
19 changed files with 374 additions and 102 deletions
-6
View File
@@ -2,12 +2,6 @@
## Unreleased
## v1.8.19-alpha (2026-09-28)
- Fixed the embedded AIUI chat page painting a second background and dark scrim over Archy’s dashboard background.
- Embedded AIUI now stays transparent so the dashboard background appears once.
- AIUI background fixes are now included reliably in OTA updates and fresh installations.
## v1.8.18-alpha (2026-09-18)
- Framework startup prioritizes Bitcoin and LND before unrelated containers, and unavailable LND balances remain unavailable instead of appearing as false zeroes.
+2 -3
View File
@@ -46,14 +46,13 @@ interface RateBucket {
const rateBuckets = new Map<string, RateBucket>()
// Vite imports this module during builds too; cleanup must not keep the
// process alive once compilation has finished.
// Clean up stale buckets every 5 minutes
setInterval(() => {
const now = Date.now()
for (const [key, bucket] of rateBuckets) {
if (now > bucket.resetAt) rateBuckets.delete(key)
}
}, 5 * 60_000).unref()
}, 5 * 60_000)
function getClientIp(req: IncomingMessage): string {
return req.socket.remoteAddress ?? 'unknown'
-1
View File
@@ -33,7 +33,6 @@ const PWA_CACHE_VERSION = '2'
// Only embedded when explicitly requested via ?embedded param
const _embeddedFlag = new URLSearchParams(window.location.search).has('embedded')
;(window as unknown as Record<string, unknown>).__AIUI_EMBEDDED__ = _embeddedFlag
document.documentElement.classList.toggle('aiui-embedded', _embeddedFlag)
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
+6 -15
View File
@@ -57,8 +57,12 @@ body {
width: 100%;
height: 100%;
overflow: hidden;
/* Standalone canvas fallback. Embedded mode overrides this below so
Archy's wallpaper remains visible through the iframe. */
/* Every page paints its own explicit background (bg-[#0a0a0a] / bg-[#faf9f6])
EXCEPT the embedded Chat page, which intentionally goes transparent so
Archy's own dark chrome can show behind it (Chat.vue's iframe host). With
no background-color here, "transparent" fell through to the browser's
default white canvas instead. Match the theme's own dark/light default so
nothing above this ever needs to guess. */
background-color: #0a0a0a;
}
@@ -66,19 +70,6 @@ html.light body {
background-color: #faf9f6;
}
/* The host owns the wallpaper when AIUI is embedded. The document canvas
must be transparent too, otherwise it hides the host behind ChatPage. */
html.aiui-embedded {
/* Match Archy's dark canvas scheme. Browsers otherwise give an iframe
with a different scheme an opaque canvas despite transparent CSS. */
color-scheme: dark;
}
html.aiui-embedded,
html.aiui-embedded body {
background: transparent;
}
/* ===== DARK MODE GLASSMORPHISM — from Archy ===== */
@layer components {
+102
View File
@@ -0,0 +1,102 @@
app:
id: conduit-market
name: Conduit Market
version: 1.0.0
# Built by this project from a pinned upstream commit (Conduit ships no
# Dockerfile of its own) — there is no upstream release feed/tag to watch,
# so re-pin CONDUIT_COMMIT in the Dockerfile deliberately, by hand.
upstream:
kind: github
repo: Conduit-BTC/conduit-mono
description: |
Decentralized Nostr + Lightning marketplace (apps/market from
Conduit-BTC/conduit-mono). Pure client-side SPA — listings are NIP-99
events, orders are encrypted DMs, payments settle over Lightning via
NWC — confirmed by reading packages/core/src/config.ts and
apps/market/package.json directly; there is no custom backend to run.
Public/private is a genuine runtime toggle, not two separate builds:
Vite bakes VITE_* relay env vars into the bundle at compile time (see
the comment on getViteEnv() in config.ts), so a single image built once
could never be reconfigured afterward via ordinary env vars. This
package patches config.ts (docker/conduit-market/apply-runtime-override.py)
to merge in a window.__CONDUIT_RUNTIME_ENV__ override object, written by
docker-entrypoint.sh from CONDUIT_MODE/CONDUIT_PRIVATE_RELAY_URLS at
container *start*. Flipping CONDUIT_MODE and restarting the container is
enough — no rebuild required. CONDUIT_MODE=private with no relay URL set
refuses to start rather than silently falling back to the public network.
category: money
container:
build:
context: /opt/archipelago/docker/conduit-market
dockerfile: Dockerfile
tag: localhost/conduit-market:local
network: archy-net
dependencies: []
resources:
memory_limit: 64Mi
security:
capabilities: []
# false, not true: docker-entrypoint.sh regenerates runtime-env.js under
# /usr/share/nginx/html (part of the image root fs, not a volume) on
# every container start — that's the actual public/private switch, so
# the root fs must stay writable for the mode flip to take effect.
readonly_root: false
no_new_privileges: true
network_policy: bridge
ports:
- host: 8091
container: 8080
protocol: tcp
bind: 127.0.0.1
auth: none
auth_rationale: >-
Public storefront meant for anonymous shoppers, not an admin
surface — Conduit has no login of its own (identity is a Nostr
keypair held client-side) and a cookie/session gate in front of it
would just block real customers from browsing or checking out.
volumes: []
# CONDUIT_MODE (public|private) and CONDUIT_PRIVATE_RELAY_URLS are the
# actual public/private switch, read by docker-entrypoint.sh at container
# start — see the description above. Defaults here are the safe/inert
# choice (public, using Conduit's own canonical relay network); an
# operator who wants an isolated single-relay storefront sets both.
environment:
- CONDUIT_MODE=public
- CONDUIT_PRIVATE_RELAY_URLS=
- CONDUIT_LIGHTNING_NETWORK=mainnet
health_check:
type: http
endpoint: http://127.0.0.1:8091
path: /health
interval: 30s
timeout: 5s
retries: 3
interfaces:
main:
name: Storefront
description: Conduit Market storefront
type: ui
port: 8091
protocol: http
path: /
metadata:
category: money
tier: optional
author: Conduit-BTC
repo: https://github.com/Conduit-BTC/conduit-mono
features:
- Nostr-native marketplace listings (NIP-99)
- Encrypted order flow over Nostr DMs
- Lightning checkout via Nostr Wallet Connect
- Runtime-configurable public or private relay mode, no rebuild
+1 -1
View File
@@ -104,7 +104,7 @@ dependencies = [
[[package]]
name = "archipelago"
version = "1.8.19-alpha"
version = "1.8.18-alpha"
dependencies = [
"anyhow",
"archipelago-container",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "archipelago"
version = "1.8.19-alpha"
version = "1.8.18-alpha"
edition = "2021"
license.workspace = true
description = "Archipelago Bitcoin Node OS - Native backend"
+2 -8
View File
@@ -514,13 +514,6 @@ impl MintClient {
pub async fn swap(&self, inputs: &[Proof], target_amounts: &[u64]) -> Result<SwapResult> {
let keyset = self.get_active_sat_keyset().await?;
// cashuB tokens carry NUT-02 v2 keyset ids in their 8-byte short form,
// which the mint rejects (bare 422). Repair here, not at each caller:
// the paid-download seller path swapped directly and every Minibits
// payment failed once the mint rotated to a v2 keyset.
let repaired = self.resolve_truncated_keyset_ids(inputs).await;
let inputs: &[Proof] = &repaired;
// NUT-02: a mint may charge a per-input fee, and it rejects the swap
// outright unless outputs == inputs - fee (`11005 Transaction inputs
// should equal outputs less fee`). Applied here rather than at each
@@ -820,7 +813,8 @@ impl MintClient {
let total: u64 = entry.proofs.iter().map(|p| p.amount).sum();
let target_amounts = amount_to_denominations(total);
let result = self.swap(&entry.proofs, &target_amounts).await?;
let proofs = self.resolve_truncated_keyset_ids(&entry.proofs).await;
let result = self.swap(&proofs, &target_amounts).await?;
all_new_proofs.extend(result.new_proofs);
}
+62
View File
@@ -0,0 +1,62 @@
# syntax=docker/dockerfile:1.6
#
# Packages Conduit Market (github.com/Conduit-BTC/conduit-mono, apps/market)
# as a static Archipelago app. Conduit itself is a pure client-side SPA --
# products are Nostr events (NIP-99), orders are encrypted DMs, payments go
# over Lightning via NWC -- no custom backend, confirmed by reading
# packages/core/src/config.ts and apps/market/package.json directly rather
# than assumed. Upstream ships no Dockerfile; this one is new.
#
# Pinned to a specific upstream commit (not a moving branch) for
# reproducibility, matching the archy convention already used for
# apps/cuprate. Re-pin deliberately, not on every rebuild.
#
# oven/bun:1.1-slim is pinned to an EOL Debian bullseye base whose
# -security pool no longer serves the package versions it references
# (404s on ca-certificates/curl et al). oven/bun:1-slim currently resolves
# to bun 1.4.x on Debian trixie (current), so use that instead -- still a
# floating minor tag, but the alternative (hand-pinning a bullseye/bookworm
# digest) just reintroduces the same staleness problem later.
FROM oven/bun:1-slim AS build
ARG CONDUIT_COMMIT=c6606382dda8953763646f4498b2c4a4103da0a3
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl python3 \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL "https://github.com/Conduit-BTC/conduit-mono/archive/${CONDUIT_COMMIT}.tar.gz" \
-o /tmp/conduit.tar.gz \
&& tar xzf /tmp/conduit.tar.gz --strip-components=1 \
&& rm /tmp/conduit.tar.gz
# Full workspace install: apps/market depends on @conduit/core and
# @conduit/ui via workspace:*, so bun needs the whole monorepo present to
# link them, even though only the market app actually gets built below.
RUN bun install --frozen-lockfile
COPY apply-runtime-override.py /build/apply-runtime-override.py
RUN python3 apply-runtime-override.py
# Loads window.__CONDUIT_RUNTIME_ENV__ (written by docker-entrypoint.sh at
# container start) before the app bundle runs. A plain (non-module) script
# tag with an absolute path: Vite copies these through to dist/index.html
# verbatim without trying to resolve them as part of the module graph, and
# does not require the file to exist in the source tree at build time.
RUN sed -i 's#<script type="module" src="/src/main.tsx"></script>#<script src="/runtime-env.js"></script>\n <script type="module" src="/src/main.tsx"></script>#' \
apps/market/index.html
# Leave every VITE_* relay/network var unset here on purpose -- the runtime
# override (see above) is the actual public/private switch; baking any of
# these in at build time would defeat the point of a live-configurable
# single image.
RUN bun run --filter '@conduit/market' build
FROM nginx:1.27-alpine
COPY --from=build /build/apps/market/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY docker-entrypoint.sh /docker-entrypoint.d/50-conduit-runtime-env.sh
RUN chmod +x /docker-entrypoint.d/50-conduit-runtime-env.sh
EXPOSE 8080
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Patches packages/core/src/config.ts so it can be reconfigured
public/private at container-start time instead of only at build time.
Vite bakes VITE_* env vars into the bundle as literal strings when it
compiles (import.meta.env.VITE_FOO is a static replacement, not a runtime
lookup) -- confirmed directly in config.ts's own comment on getViteEnv().
That means a container image built once can never be flipped between an
isolated private relay and the public Conduit network via ordinary env
vars after the fact; the values are frozen into the compiled JS.
This packaging needs exactly that live toggle, so docker-entrypoint.sh
writes a small /runtime-env.js at *container start* (from real env vars)
setting window.__CONDUIT_RUNTIME_ENV__ before the app bundle runs. This
script inserts one small override function right before the single call
site that consumes getViteEnv()'s result (`const env = getViteEnv()`,
confirmed to be the only call site in the file and to run once at module
load), so only that one merge point needs to change. Only non-empty
string fields override the Vite-baked default; a build with no runtime
override behaves identically to stock upstream Conduit.
Uses a literal string anchor rather than a line-numbered diff/patch --
more robust against upstream reformatting a comment or reflowing
unrelated lines than a traditional unified diff would be.
"""
import sys
CONFIG_PATH = "packages/core/src/config.ts"
ANCHOR = "const env = getViteEnv()"
OVERRIDE_BLOCK = '''// --- Archipelago runtime relay override -------------------------------------
// See docker/conduit-market/apply-runtime-override.py in the archy repo for
// why this exists: Vite bakes VITE_* env vars into the bundle at build
// time, so a container image built once can never be reconfigured
// public/private afterward via ordinary env vars. docker-entrypoint.sh
// writes window.__CONDUIT_RUNTIME_ENV__ from real environment variables at
// container start, before this bundle runs.
function getRuntimeOverrides(): Partial<ReturnType<typeof getViteEnv>> {
if (typeof window === "undefined") return {}
const raw = (window as unknown as Record<string, unknown>)
.__CONDUIT_RUNTIME_ENV__
if (!raw || typeof raw !== "object") return {}
const out: Record<string, string> = {}
for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
if (typeof value === "string" && value.length > 0) out[key] = value
}
return out as Partial<ReturnType<typeof getViteEnv>>
}
const env = { ...getViteEnv(), ...getRuntimeOverrides() }'''
with open(CONFIG_PATH) as f:
content = f.read()
count = content.count(ANCHOR)
if count != 1:
print(
f"FATAL: expected exactly 1 occurrence of anchor line in {CONFIG_PATH}, "
f"found {count}. Upstream config.ts has likely changed in a way that "
"needs this script re-checked by hand before it can safely patch it.",
file=sys.stderr,
)
sys.exit(1)
content = content.replace(ANCHOR, OVERRIDE_BLOCK, 1)
with open(CONFIG_PATH, "w") as f:
f.write(content)
print(f"Patched {CONFIG_PATH}: runtime relay override installed.")
@@ -0,0 +1,43 @@
#!/bin/sh
# Runs via nginx's own /docker-entrypoint.d/ hook mechanism (official nginx
# image sources every executable script there before starting nginx) --
# no custom ENTRYPOINT needed. Writes /runtime-env.js, loaded by index.html
# before the app bundle (see Dockerfile), so this container's own env vars
# can flip Conduit between an isolated private relay and the public Conduit
# network without rebuilding the image -- see
# packages/core/src/config.ts's getRuntimeOverrides() (installed by
# apply-runtime-override.py) for the other half of this.
set -eu
CONDUIT_MODE="${CONDUIT_MODE:-public}"
OUT=/usr/share/nginx/html/runtime-env.js
if [ "$CONDUIT_MODE" = "private" ]; then
if [ -z "${CONDUIT_PRIVATE_RELAY_URLS:-}" ]; then
echo "conduit-market: CONDUIT_MODE=private requires CONDUIT_PRIVATE_RELAY_URLS (comma-separated wss:// URLs) -- refusing to start with no relay configured" >&2
exit 1
fi
cat > "$OUT" <<EOF
window.__CONDUIT_RUNTIME_ENV__ = {
relayUrl: "",
defaultRelayUrl: "${CONDUIT_PRIVATE_RELAY_URLS}",
defaultRelays: "${CONDUIT_PRIVATE_RELAY_URLS}",
appWriteRelayUrls: "${CONDUIT_PRIVATE_RELAY_URLS}",
publicRelayUrls: "${CONDUIT_PRIVATE_RELAY_URLS}",
commerceRelayUrls: "${CONDUIT_PRIVATE_RELAY_URLS}",
lightningNetwork: "${CONDUIT_LIGHTNING_NETWORK:-mainnet}"
};
EOF
echo "conduit-market: CONDUIT_MODE=private -- isolated to ${CONDUIT_PRIVATE_RELAY_URLS}"
else
# public: no relay overrides at all, so Conduit's own built-in canonical
# public relay defaults (packages/core/src/config.ts,
# CANONICAL_APP_RELAY_DEFINITIONS) apply exactly as they do upstream --
# this store fully participates in the wider Conduit network.
cat > "$OUT" <<EOF
window.__CONDUIT_RUNTIME_ENV__ = {
lightningNetwork: "${CONDUIT_LIGHTNING_NETWORK:-mainnet}"
};
EOF
echo "conduit-market: CONDUIT_MODE=public -- using Conduit's default public relay network"
fi
+28
View File
@@ -0,0 +1,28 @@
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Regenerated by docker-entrypoint.d/50-conduit-runtime-env.sh on every
# container start -- must never be cached, or a mode flip (public <->
# private) followed by a container restart would silently keep serving
# the old relay config to anyone with a warm cache.
location = /runtime-env.js {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
expires 0;
}
# Client-side routing (@tanstack/react-router) -- unknown paths fall
# through to index.html rather than 404ing.
location / {
try_files $uri $uri/ /index.html;
}
location = /health {
access_log off;
default_type text/plain;
return 200 "ok\n";
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "neode-ui",
"version": "1.8.19-alpha",
"version": "1.8.18-alpha",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "neode-ui",
"version": "1.8.19-alpha",
"version": "1.8.18-alpha",
"dependencies": {
"@scure/bip39": "^2.2.0",
"@types/dompurify": "^3.0.5",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "neode-ui",
"private": true,
"version": "1.8.19-alpha",
"version": "1.8.18-alpha",
"type": "module",
"scripts": {
"start": "./start-dev.sh",
@@ -362,18 +362,6 @@ init()
</button>
</div>
<div class="overflow-y-auto flex-1 min-h-0 space-y-6 pr-1">
<!-- v1.8.19-alpha -->
<div>
<div class="flex items-center gap-2 mb-3">
<span class="text-xs font-mono px-2 py-0.5 rounded bg-orange-500/20 text-orange-300">v1.8.19-alpha</span>
<span class="text-xs text-white/40">September 28, 2026</span>
</div>
<div class="space-y-3 text-sm text-white/80 pl-3 border-l border-white/10">
<p>Fixed the embedded AIUI chat page painting a second background and dark scrim over Archy’s dashboard background.</p>
<p>Embedded AIUI now stays transparent so the dashboard background appears once.</p>
<p>AIUI background fixes are now included reliably in OTA updates and fresh installations.</p>
</div>
</div>
<!-- v1.8.18-alpha -->
<div>
<div class="flex items-center gap-2 mb-3">
@@ -381,9 +369,9 @@ init()
<span class="text-xs text-white/40">September 18, 2026</span>
</div>
<div class="space-y-3 text-sm text-white/80 pl-3 border-l border-white/10">
<p>Framework startup prioritizes Bitcoin and LND before unrelated containers, and unavailable LND balances remain unavailable instead of appearing as false zeroes.</p>
<p>Cashu Receive guides unseeded wallets through recovery-phrase setup, with shorter backup guidance and a single-column layout.</p>
<p>Added live Framework verification for automatic LND unlock, native balance preservation, Cashu address registration, and proof preservation.</p>
<p>Framework startup now brings Bitcoin and LND up before unrelated containers, and the dashboard keeps balances unavailable instead of showing a false zero when LND is not ready.</p>
<p>Cashu wallets can set up a recovery phrase from Receive, with clearer backup and restore guidance.</p>
<p>Ecash backup guidance is shorter and arranged in a single column for easier use on small screens.</p>
</div>
</div>
<!-- v1.8.17-alpha -->
+17 -17
View File
@@ -1,29 +1,29 @@
{
"changelog": [
"Fixed the embedded AIUI chat page painting a second background and dark scrim over Archy’s dashboard background.",
"Embedded AIUI now stays transparent so the dashboard background appears once.",
"AIUI background fixes are now included reliably in OTA updates and fresh installations."
"Framework startup prioritizes Bitcoin and LND before unrelated containers, and unavailable LND balances remain unavailable instead of appearing as false zeroes.",
"Cashu Receive guides unseeded wallets through recovery-phrase setup, with shorter backup guidance and a single-column layout.",
"Added live Framework verification for automatic LND unlock, native balance preservation, Cashu address registration, and proof preservation."
],
"components": [
{
"current_version": "1.8.19-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.19-alpha/archipelago",
"current_version": "1.8.18-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.18-alpha/archipelago",
"name": "archipelago",
"new_version": "1.8.19-alpha",
"sha256": "bb500d02567ad16179179d43138cc6fdafee680835262026eeaa7d1bc8cdd307",
"size_bytes": 64495784
"new_version": "1.8.18-alpha",
"sha256": "c76415c295dd42159e45e74d93f7c2f6fa39b0351946a8303f8cfba1dd3a6087",
"size_bytes": 64497240
},
{
"current_version": "1.8.19-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.19-alpha/archipelago-frontend-1.8.19-alpha.tar.gz",
"name": "archipelago-frontend-1.8.19-alpha.tar.gz",
"new_version": "1.8.19-alpha",
"sha256": "fbbaa237e2ea4e9e576dda9b15fdcf3c59c40bc55bfee4ebbea303b0172fc49b",
"size_bytes": 97142031
"current_version": "1.8.18-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.18-alpha/archipelago-frontend-1.8.18-alpha.tar.gz",
"name": "archipelago-frontend-1.8.18-alpha.tar.gz",
"new_version": "1.8.18-alpha",
"sha256": "57da88b1a8f3d2841867994a95e9e4e728c19616abcde8e3d3f151c6d94ed501",
"size_bytes": 98801020
}
],
"release_date": "2026-09-28",
"signature": "4da9bf766d94c2de6641619a36663106791c7a1d342b729c666662cdde1feabdf11c51994c1cbbe2a0e2b270c316d77763435e976ce22730855b3822bc9d390a",
"release_date": "2026-09-20",
"signature": "84e0e1a20750f0d4ff27ba539c2e9424a5b02c19a5689f73cdf82813212acae19e0af244a62e66ac37a35d97d80deb2833819d809b91a7822f2bfd785d3fe30d",
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
"version": "1.8.19-alpha"
"version": "1.8.18-alpha"
}
+17 -17
View File
@@ -1,29 +1,29 @@
{
"changelog": [
"Fixed the embedded AIUI chat page painting a second background and dark scrim over Archy’s dashboard background.",
"Embedded AIUI now stays transparent so the dashboard background appears once.",
"AIUI background fixes are now included reliably in OTA updates and fresh installations."
"Framework startup prioritizes Bitcoin and LND before unrelated containers, and unavailable LND balances remain unavailable instead of appearing as false zeroes.",
"Cashu Receive guides unseeded wallets through recovery-phrase setup, with shorter backup guidance and a single-column layout.",
"Added live Framework verification for automatic LND unlock, native balance preservation, Cashu address registration, and proof preservation."
],
"components": [
{
"current_version": "1.8.19-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.19-alpha/archipelago",
"current_version": "1.8.18-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.18-alpha/archipelago",
"name": "archipelago",
"new_version": "1.8.19-alpha",
"sha256": "bb500d02567ad16179179d43138cc6fdafee680835262026eeaa7d1bc8cdd307",
"size_bytes": 64495784
"new_version": "1.8.18-alpha",
"sha256": "c76415c295dd42159e45e74d93f7c2f6fa39b0351946a8303f8cfba1dd3a6087",
"size_bytes": 64497240
},
{
"current_version": "1.8.19-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.19-alpha/archipelago-frontend-1.8.19-alpha.tar.gz",
"name": "archipelago-frontend-1.8.19-alpha.tar.gz",
"new_version": "1.8.19-alpha",
"sha256": "fbbaa237e2ea4e9e576dda9b15fdcf3c59c40bc55bfee4ebbea303b0172fc49b",
"size_bytes": 97142031
"current_version": "1.8.18-alpha",
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.18-alpha/archipelago-frontend-1.8.18-alpha.tar.gz",
"name": "archipelago-frontend-1.8.18-alpha.tar.gz",
"new_version": "1.8.18-alpha",
"sha256": "57da88b1a8f3d2841867994a95e9e4e728c19616abcde8e3d3f151c6d94ed501",
"size_bytes": 98801020
}
],
"release_date": "2026-09-28",
"signature": "4da9bf766d94c2de6641619a36663106791c7a1d342b729c666662cdde1feabdf11c51994c1cbbe2a0e2b270c316d77763435e976ce22730855b3822bc9d390a",
"release_date": "2026-09-20",
"signature": "84e0e1a20750f0d4ff27ba539c2e9424a5b02c19a5689f73cdf82813212acae19e0af244a62e66ac37a35d97d80deb2833819d809b91a7822f2bfd785d3fe30d",
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
"version": "1.8.19-alpha"
"version": "1.8.18-alpha"
}
+8 -10
View File
@@ -78,17 +78,15 @@ if [ -z "$FRONTEND_ARCHIVE" ]; then
STAGING_DIR=$(mktemp -d -t archipelago-frontend.XXXXXX)
echo "Staging frontend archive in $STAGING_DIR..."
cp -r "$FRONTEND_DIST/." "$STAGING_DIR/"
# create-release.sh folds the freshly built AIUI into FRONTEND_DIST.
# Never overlay it with the older demo bundle (or nest aiui/aiui/).
if [ ! -f "$STAGING_DIR/aiui/index.html" ] || \
[ ! -f "$STAGING_DIR/aiui/BUILD-INFO" ]; then
echo "Error: fresh AIUI payload missing from frontend dist" >&2
exit 1
# Bake AIUI in so fresh installs pick it up. OTA already
# carries-forward the existing aiui/ if the tarball lacks one
# (update.rs:922), but including it here makes the tarball
# the single source of truth instead of relying on a side-
# effect of the in-place swap.
if [ -d "$PROJECT_ROOT/demo/aiui" ] && [ -f "$PROJECT_ROOT/demo/aiui/index.html" ]; then
echo " Including AIUI from demo/aiui/"
cp -r "$PROJECT_ROOT/demo/aiui" "$STAGING_DIR/aiui"
fi
grep -Fxq "commit=$(git -C "$PROJECT_ROOT" rev-parse HEAD)" "$STAGING_DIR/aiui/BUILD-INFO" || {
echo "Error: AIUI payload was not built from the current commit" >&2
exit 1
}
# OTA bridge for nodes running older updaters: they only know how to
# apply the backend binary and frontend archive. Carry host runtime
# assets inside the frontend tarball; the new backend promotes them
+9 -5
View File
@@ -169,11 +169,15 @@ else
fi
cd "$PROJECT_ROOT"
# Build AIUI from the same source as the release. The checked-in demo bundle
# can predate source fixes and must never overwrite the production payload.
bash "$SCRIPT_DIR/build-aiui.sh"
rm -rf "$PROJECT_ROOT/web/dist/neode-ui/aiui"
cp -r "$PROJECT_ROOT/aiui/packages/app/dist" "$PROJECT_ROOT/web/dist/neode-ui/aiui"
# npm run build wipes web/dist — fold AIUI straight back in. The OTA tarball
# bakes it from demo/aiui independently, but build-iso-release.sh's
# verify-artifacts guard checks web/dist/neode-ui/aiui and failed on two
# consecutive releases (.127, .129) because this fold-in was manual.
if [ -d "$PROJECT_ROOT/demo/aiui" ] && [ -f "$PROJECT_ROOT/demo/aiui/index.html" ]; then
rm -rf "$PROJECT_ROOT/web/dist/neode-ui/aiui"
cp -r "$PROJECT_ROOT/demo/aiui" "$PROJECT_ROOT/web/dist/neode-ui/aiui"
echo " AIUI folded into web/dist from demo/aiui"
fi
# npm run build can silently no-op (vue-tsc EACCES burned us before) — a stale
# dist would ship with a perfectly valid sha256. Require the freshly built