Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb0193932f | ||
|
|
63cb68451a | ||
|
|
17cfebbe26 | ||
|
|
379fb930fc | ||
|
|
1bebdeac0f | ||
|
|
f458591132 | ||
|
|
6155539254 | ||
|
|
76e0f1f3b6 | ||
|
|
ba6ce2cdb6 | ||
|
|
8212049f57 |
@@ -2,6 +2,12 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 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.
|
||||
- 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.
|
||||
|
||||
## v1.8.17-alpha (2026-09-15)
|
||||
|
||||
- Minibits claims that every mint reports as already spent leave the retry queue, clearing repeated failure notices. Network errors and mixed mint failures remain queued for another attempt.
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<div
|
||||
class="h-full flex flex-col relative overflow-hidden transition-colors duration-300"
|
||||
:class="[]"
|
||||
:style="isDark
|
||||
? { background: '#000 url(' + bgImageUrl + ') center center / cover no-repeat fixed' }
|
||||
: isEmbedded
|
||||
:style="isEmbedded
|
||||
? { background: 'transparent' }
|
||||
: isDark
|
||||
? { background: '#000 url(' + bgImageUrl + ') center center / cover no-repeat fixed' }
|
||||
: { backgroundColor: '#f5f4f1' }"
|
||||
>
|
||||
<div v-if="isDark" class="absolute inset-0 pointer-events-none bg-black/20" />
|
||||
<div v-if="isDark && !isEmbedded" class="absolute inset-0 pointer-events-none bg-black/20" />
|
||||
|
||||
<!-- Desktop layout -->
|
||||
<div
|
||||
|
||||
@@ -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
|
||||
Generated
+1
-1
@@ -104,7 +104,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "archipelago"
|
||||
version = "1.8.17-alpha"
|
||||
version = "1.8.18-alpha"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"archipelago-container",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "archipelago"
|
||||
version = "1.8.17-alpha"
|
||||
version = "1.8.18-alpha"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Archipelago Bitcoin Node OS - Native backend"
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -286,3 +286,93 @@ record final human confirmation of the rendered dashboard balance (native balanc
|
||||
match exactly, and UI failure/recovery regressions pass). Keep this follow-up
|
||||
visible across sessions; do not rebuild/reboot/reinitialize a working wallet just
|
||||
to repeat already completed checks.
|
||||
|
||||
### Backup copy and layout — 2026-09-15
|
||||
|
||||
At the user's request, shortened the ecash backup explanations and stacked each
|
||||
card section's text and full-width action vertically. Kept the distinction
|
||||
between node-derived and separate phrases, and the warning that a newly created
|
||||
phrase covers future coins rather than existing legacy coins.
|
||||
|
||||
All 10 Receive/backup tests and the production UI build pass. Deployed the UI to
|
||||
Framework without a restart; served index and backup-component bundle match the
|
||||
build byte-for-byte. The prior UI is saved as `web-ui-before-backup-copy` in the
|
||||
protected incident directory. Source integration and final rendered dashboard
|
||||
balance confirmation remain pending as above.
|
||||
|
||||
### LNURL comment-length report — 2026-09-15
|
||||
|
||||
User reports a maximum-comment-length error in some sending wallets. Live
|
||||
Framework address metadata advertises integer `commentAllowed: 100`. The QR
|
||||
contains the address only; Archy's Receive UI does not add a comment. The
|
||||
Minibits-hosted callback returned invoices for omitted/empty comments, 100 ASCII
|
||||
characters, 101 ASCII characters, and 100 accented characters. These were unpaid
|
||||
invoice requests at the advertised minimum amount; no funds were sent.
|
||||
|
||||
The callback did not reproduce the error, including beyond its advertised limit.
|
||||
Sending-wallet validation against the advertised 100-character limit is therefore
|
||||
a hypothesis, not a confirmed root cause. Asked which wallets fail and whether
|
||||
an empty comment also fails. Need that result before selecting a code fix.
|
||||
The service controls the advertised limit; changing local Receive text or QR
|
||||
cannot raise it for other wallets.
|
||||
|
||||
### Primal Spark: automatic recipient note exceeds the address limit
|
||||
|
||||
User clarified that no comment was entered and the sender is Primal Spark.
|
||||
Checked Framework's management journal over the preceding 20 minutes: no
|
||||
comment-length errors, service active, and zero pending Minibits claims. Recent
|
||||
claim polling connected to and disconnected from the relay normally. Historical
|
||||
seed-authentication failures preceded the successful setup already documented.
|
||||
|
||||
The live address's Minibits `text/plain` description is **101 ASCII characters**,
|
||||
while `commentAllowed` is **100**. Description template (address redacted):
|
||||
`Pay to [ADDRESS] with Lightning. Receiver will receive ecash into Minibits Wallet.`
|
||||
|
||||
Primal Android source at `36939db97213e7f8eeefaa4adaf125d839fc662e`:
|
||||
- `WalletTextParserImpl.handleLnUrlText` assigns the parsed description to
|
||||
`DraftTx.noteRecipient`, including for Lightning-address input.
|
||||
- `TransactionEditor` initializes its editable recipient note from that value.
|
||||
- `SparkWalletServiceImpl` passes it untrimmed to `PrepareLnurlPayRequest.comment`.
|
||||
- Breez Spark source at `8bb38ec292a590907360c4e7f2a4134b8f09de9e`,
|
||||
`common/src/lnurl/pay.rs::validate_user_input`, rejects a comment exceeding the
|
||||
limit with the exact reported error before requesting the callback.
|
||||
|
||||
This identifies a concrete compatibility failure: the address description can
|
||||
become an automatic over-limit comment without the sender typing anything.
|
||||
The user confirmed that explicitly clearing the prefilled recipient note made
|
||||
the payment work, and supplied the same description observed in live metadata.
|
||||
This confirms the automatic-comment compatibility failure. The installed Primal
|
||||
platform/version was not captured. Node logs alone cannot show sender-side
|
||||
validation or requests to the external Minibits callback.
|
||||
|
||||
Durable upstream correction: Primal should keep receiver metadata separate from
|
||||
the sender's comment and enforce the limit on actual user comments. Minibits can
|
||||
also shorten its description or raise its advertised comment limit. Archy does
|
||||
not serve this external LNURL metadata; do not rename an existing wallet address,
|
||||
rotate its seed, or claim that a local dashboard edit fixes this sender behavior.
|
||||
|
||||
### Primal workaround confirmed by user
|
||||
|
||||
The user confirmed successful payment after removing the automatic description.
|
||||
The permanent sender-side correction is to leave the recipient comment empty by
|
||||
default and retain receiver metadata only as display text. In Primal Android,
|
||||
remove the assignment of the LNURL description to the draft recipient note in
|
||||
`WalletTextParserImpl.handleLnUrlText`; also validate explicitly entered comments
|
||||
against the endpoint's limit. No upstream change has been submitted or deployed.
|
||||
Existing Framework addresses and wallet identities remain unchanged.
|
||||
|
||||
### Can Archy shorten the current address description?
|
||||
|
||||
Inspected Minibits' public wallet client (`src/services/minibitsService.ts`,
|
||||
`updateWalletProfile`) and `WalletProfileRecord`. The supported profile update
|
||||
fields are name, lud16, and avatar; there is no exposed LNURL description or
|
||||
comment-limit setting. Its public web repository also contains no implementation
|
||||
of the LNURL metadata endpoint or description template.
|
||||
|
||||
For the existing `@minibits.cash` address, no supported client-side mechanism
|
||||
to shorten this text was found. Do not send guessed profile-update fields or
|
||||
rename the address to disguise the problem. A Minibits server change could use
|
||||
`Pay to [ADDRESS]`, well below the current limit. Controlling this metadata in
|
||||
Archy would instead require an Archy-hosted LNURL service/address and correct
|
||||
invoice metadata binding; rewriting the QR label or only proxying edited metadata
|
||||
is insufficient. No wallet/profile mutations were made during this investigation.
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "neode-ui",
|
||||
"version": "1.8.17-alpha",
|
||||
"version": "1.8.18-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "neode-ui",
|
||||
"version": "1.8.17-alpha",
|
||||
"version": "1.8.18-alpha",
|
||||
"dependencies": {
|
||||
"@scure/bip39": "^2.2.0",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "neode-ui",
|
||||
"private": true,
|
||||
"version": "1.8.17-alpha",
|
||||
"version": "1.8.18-alpha",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "./start-dev.sh",
|
||||
|
||||
@@ -226,61 +226,54 @@ async function restoreFromPhrase() {
|
||||
Your ecash has no backup yet
|
||||
</div>
|
||||
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-xl font-semibold text-white/96 mb-1">{{ setupOnly ? 'Set up your Cashu Lightning address' : 'Ecash backup phrase' }}</h2>
|
||||
|
||||
<p v-if="status?.active && status?.source === 'node-seed'" class="text-sm text-white/60">
|
||||
Your ecash wallet has its own 24-word phrase, derived from this node's recovery
|
||||
phrase — so the words you already wrote down cover your ecash too. Reveal it here
|
||||
if you want to restore your ecash into another wallet (Minibits, Nutstash,
|
||||
<span class="font-mono">cdk-cli</span>) without handing over the node's own seed.
|
||||
<p v-if="status?.active && status?.source === 'node-seed'" class="text-sm leading-relaxed text-white/60">
|
||||
Your node's recovery phrase also recovers this ecash phrase. Reveal its 24 words
|
||||
to restore in a compatible Cashu wallet without sharing your node's phrase.
|
||||
</p>
|
||||
<p v-else-if="status?.active" class="text-sm text-white/60">
|
||||
Your ecash wallet has its own 24-word phrase. Reveal it to write it down, or to
|
||||
restore your ecash into another wallet (Minibits, Nutstash,
|
||||
<span class="font-mono">cdk-cli</span>).
|
||||
<p v-else-if="status?.active" class="text-sm leading-relaxed text-white/60">
|
||||
Save your 24-word ecash phrase to restore this wallet here or in another
|
||||
compatible Cashu wallet.
|
||||
</p>
|
||||
<p v-else class="text-sm text-white/60">
|
||||
Ecash is a bearer instrument: the coins live in a file on this node, and right now
|
||||
nothing can bring them back if that file is lost. Setting up a backup phrase fixes
|
||||
that for every coin minted from then on.
|
||||
<p v-else class="text-sm leading-relaxed text-white/60">
|
||||
If this node's coin file is lost, your ecash is lost. Set up a phrase to recover
|
||||
future coins; existing coins aren't covered.
|
||||
<template v-if="status?.derivable_from_node_seed">
|
||||
It's derived from this node's recovery phrase, so there's nothing new to write down.
|
||||
Your node's recovery phrase will also recover this phrase.
|
||||
</template>
|
||||
<template v-else>
|
||||
This node has no encrypted seed backup to derive from, so the phrase will be its
|
||||
own — you'll need to write these words down and keep them.
|
||||
This node has no saved seed, so write down and keep the new phrase separately.
|
||||
</template>
|
||||
</p>
|
||||
|
||||
<p v-if="status?.source === 'independent' || status?.source === 'imported'" class="mt-2 text-xs text-orange-300/90">
|
||||
This wallet's phrase was <strong>not</strong> derived from the node's recovery
|
||||
phrase{{ status?.source === 'imported' ? ' — it was imported' : '' }}, so restoring
|
||||
the node will not bring the ecash back. Only these words will.
|
||||
{{ status?.source === 'imported' ? 'This imported phrase' : 'This phrase' }} is separate
|
||||
from your node's backup. <strong>Only these words recover this ecash wallet.</strong>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="shrink-0 glass-button rounded-lg px-4 py-2 text-sm font-medium"
|
||||
class="w-full glass-button rounded-lg px-4 py-2 text-sm font-medium"
|
||||
:class="!status?.active ? 'bg-orange-500/20 border-orange-400/30' : ''"
|
||||
@click="openReveal"
|
||||
>{{ status?.active ? 'Reveal' : (setupOnly ? 'Set up address' : 'Set up backup') }}</button>
|
||||
</div>
|
||||
|
||||
<div v-if="status?.active && !setupOnly" class="mt-4 pt-4 border-t border-white/10">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<p class="text-sm text-white/60 min-w-0">
|
||||
<div class="flex flex-col gap-3">
|
||||
<p class="text-sm leading-relaxed text-white/60 min-w-0">
|
||||
<span class="text-white/80 font-medium">Restore from this phrase.</span>
|
||||
Asks your mint which coins it has signed for these words and puts back any that
|
||||
are still unspent. Safe to run at any time — it never duplicates coins you already
|
||||
hold.
|
||||
Recover unspent coins from your mint. Safe to repeat; coins you already hold
|
||||
won't be duplicated.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="shrink-0 glass-button rounded-lg px-4 py-2 text-sm font-medium disabled:opacity-50"
|
||||
class="w-full glass-button rounded-lg px-4 py-2 text-sm font-medium disabled:opacity-50"
|
||||
:disabled="restoring"
|
||||
@click="restoreFromPhrase"
|
||||
>{{ restoring ? 'Scanning…' : 'Restore' }}</button>
|
||||
@@ -290,15 +283,15 @@ async function restoreFromPhrase() {
|
||||
</div>
|
||||
|
||||
<div v-if="!setupOnly" class="mt-4 pt-4 border-t border-white/10">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<p class="text-sm text-white/60 min-w-0">
|
||||
<div class="flex flex-col gap-3">
|
||||
<p class="text-sm leading-relaxed text-white/60 min-w-0">
|
||||
<span class="text-white/80 font-medium">Use a phrase from another wallet.</span>
|
||||
Point this wallet at a phrase you already have — from Minibits, Nutstash or
|
||||
<span class="font-mono">cdk-cli</span> — so its coins can be restored here.
|
||||
Import a phrase from Minibits, Nutstash or <span class="font-mono">cdk-cli</span>
|
||||
to restore its coins here.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="shrink-0 glass-button rounded-lg px-4 py-2 text-sm font-medium"
|
||||
class="w-full glass-button rounded-lg px-4 py-2 text-sm font-medium"
|
||||
@click="openImport"
|
||||
>Import</button>
|
||||
</div>
|
||||
@@ -324,7 +317,7 @@ async function restoreFromPhrase() {
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<p class="text-sm text-white/60 mb-4">
|
||||
<p class="text-sm leading-relaxed text-white/60 mb-4">
|
||||
Paste the 24-word phrase from the other wallet. The coins already in this wallet
|
||||
stay spendable either way.
|
||||
</p>
|
||||
@@ -381,9 +374,8 @@ async function restoreFromPhrase() {
|
||||
</h3>
|
||||
|
||||
<template v-if="revealedWords.length === 0">
|
||||
<p class="text-sm text-white/60 mb-4">
|
||||
Confirm your credentials to
|
||||
{{ status?.active ? 'display the 24-word ecash phrase' : 'derive and display your ecash backup phrase' }}.
|
||||
<p class="text-sm leading-relaxed text-white/60 mb-4">
|
||||
Confirm your credentials to {{ status?.active ? 'reveal' : 'set up' }} your ecash phrase.
|
||||
</p>
|
||||
<form @submit.prevent="submitReveal" class="space-y-3">
|
||||
<div>
|
||||
@@ -412,12 +404,12 @@ async function restoreFromPhrase() {
|
||||
<SeedRevealPanel :words="revealedWords" />
|
||||
<p class="text-xs text-white/40 mt-3">
|
||||
<template v-if="revealedSource === 'node-seed'">
|
||||
Derived from this node's recovery phrase — restoring the node restores this
|
||||
ecash wallet too. These words also restore it into any NUT-13 wallet.
|
||||
Your node's recovery phrase recovers this ecash wallet too. Use these words
|
||||
separately in a compatible Cashu (NUT-13) wallet.
|
||||
</template>
|
||||
<template v-else>
|
||||
This phrase is independent of the node's recovery phrase. It is the
|
||||
<strong>only</strong> way to restore this ecash wallet — write it down.
|
||||
Write these words down. They are the <strong>only</strong> way to recover
|
||||
this ecash wallet; your node's phrase won't recover it.
|
||||
</template>
|
||||
</p>
|
||||
<div class="flex gap-2 pt-4">
|
||||
|
||||
@@ -362,6 +362,18 @@ init()
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-y-auto flex-1 min-h-0 space-y-6 pr-1">
|
||||
<!-- v1.8.18-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.18-alpha</span>
|
||||
<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 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 -->
|
||||
<div>
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
|
||||
+17
-18
@@ -1,30 +1,29 @@
|
||||
{
|
||||
"changelog": [
|
||||
"Minibits claims that every mint reports as already spent leave the retry queue, clearing repeated failure notices. Network errors and mixed mint failures remain queued for another attempt.",
|
||||
"Minibits polls its primary relay first and connects to public fallback relays only when the primary is unreachable, reducing unnecessary connections.",
|
||||
"Large payment backlogs are fetched from newest to oldest with a saved cursor, so polling can resume after interruptions or page limits. Payments sharing the same timestamp remain reachable.",
|
||||
"Added regression coverage for spent-claim classification, wrapped and mixed mint errors, same-second payments, and interrupted or multi-poll backlogs."
|
||||
"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.17-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.17-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.17-alpha",
|
||||
"sha256": "32a7b009eb58f8c9f256e6597711a77ded11e15d5865a3fe16901603264e1f70",
|
||||
"size_bytes": 64953344
|
||||
"new_version": "1.8.18-alpha",
|
||||
"sha256": "c76415c295dd42159e45e74d93f7c2f6fa39b0351946a8303f8cfba1dd3a6087",
|
||||
"size_bytes": 64497240
|
||||
},
|
||||
{
|
||||
"current_version": "1.8.17-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.17-alpha/archipelago-frontend-1.8.17-alpha.tar.gz",
|
||||
"name": "archipelago-frontend-1.8.17-alpha.tar.gz",
|
||||
"new_version": "1.8.17-alpha",
|
||||
"sha256": "faf692e9a0e16268357bcac2bf86b62950ae49663e3c95982e54a132bb761980",
|
||||
"size_bytes": 98801608
|
||||
"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-15",
|
||||
"signature": "c8196fe278a5747b3c3ba3bf70998874f1e3e6eedbdab33b9e33c3339a3769ab4431f41d99924ec4cdd15a5ffed299a5af786c7ab5e9d084cdc11beabbee9103",
|
||||
"release_date": "2026-09-20",
|
||||
"signature": "84e0e1a20750f0d4ff27ba539c2e9424a5b02c19a5689f73cdf82813212acae19e0af244a62e66ac37a35d97d80deb2833819d809b91a7822f2bfd785d3fe30d",
|
||||
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
|
||||
"version": "1.8.17-alpha"
|
||||
"version": "1.8.18-alpha"
|
||||
}
|
||||
|
||||
+17
-18
@@ -1,30 +1,29 @@
|
||||
{
|
||||
"changelog": [
|
||||
"Minibits claims that every mint reports as already spent leave the retry queue, clearing repeated failure notices. Network errors and mixed mint failures remain queued for another attempt.",
|
||||
"Minibits polls its primary relay first and connects to public fallback relays only when the primary is unreachable, reducing unnecessary connections.",
|
||||
"Large payment backlogs are fetched from newest to oldest with a saved cursor, so polling can resume after interruptions or page limits. Payments sharing the same timestamp remain reachable.",
|
||||
"Added regression coverage for spent-claim classification, wrapped and mixed mint errors, same-second payments, and interrupted or multi-poll backlogs."
|
||||
"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.17-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.17-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.17-alpha",
|
||||
"sha256": "32a7b009eb58f8c9f256e6597711a77ded11e15d5865a3fe16901603264e1f70",
|
||||
"size_bytes": 64953344
|
||||
"new_version": "1.8.18-alpha",
|
||||
"sha256": "c76415c295dd42159e45e74d93f7c2f6fa39b0351946a8303f8cfba1dd3a6087",
|
||||
"size_bytes": 64497240
|
||||
},
|
||||
{
|
||||
"current_version": "1.8.17-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.17-alpha/archipelago-frontend-1.8.17-alpha.tar.gz",
|
||||
"name": "archipelago-frontend-1.8.17-alpha.tar.gz",
|
||||
"new_version": "1.8.17-alpha",
|
||||
"sha256": "faf692e9a0e16268357bcac2bf86b62950ae49663e3c95982e54a132bb761980",
|
||||
"size_bytes": 98801608
|
||||
"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-15",
|
||||
"signature": "c8196fe278a5747b3c3ba3bf70998874f1e3e6eedbdab33b9e33c3339a3769ab4431f41d99924ec4cdd15a5ffed299a5af786c7ab5e9d084cdc11beabbee9103",
|
||||
"release_date": "2026-09-20",
|
||||
"signature": "84e0e1a20750f0d4ff27ba539c2e9424a5b02c19a5689f73cdf82813212acae19e0af244a62e66ac37a35d97d80deb2833819d809b91a7822f2bfd785d3fe30d",
|
||||
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
|
||||
"version": "1.8.17-alpha"
|
||||
"version": "1.8.18-alpha"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user