Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6672d978f7 | ||
|
|
a80712963c | ||
|
|
76e6f06995 | ||
|
|
b7ba35477c | ||
|
|
e87e8017bf | ||
|
|
6168f6a7d0 | ||
|
|
fdd26ba1e3 | ||
|
|
a7a6528b08 | ||
|
|
e751b7c6f9 | ||
|
|
a500a75235 | ||
|
|
71a57c3ec1 |
+2
-1
@@ -1,7 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
## v1.8.0-alpha (2026-08-12)
|
||||
|
||||
- **Archipelago is now open source.** The full source code of the node you are running — the orchestrator, the dashboard, the app platform, the mesh, the release tooling — is published for anyone to read, build and audit at source.archipelago-foundation.org/lfg2025/archy. A node that holds your money, your files and your communications should not ask to be taken on faith: from this release onward you, or anyone you trust, can see exactly what it does and follow every change we make in the open.
|
||||
- **Installing an update is reliable again, and tells you what happened when it isn't.** Some nodes could download an update but never apply it — the button stayed on "Install", and no amount of retrying worked. The cause: applying the update consumed the downloaded files as it went, so if any one step hit a snag partway through, the leftover files were incomplete and every later attempt failed the safety re-check forever, needing a technician to recover. Applying no longer consumes the download — a failed apply can always be retried from the same files — and the pieces are now applied in a fixed order with the program itself last, so a hiccup can't leave a half-swapped node. When an apply does fail, the screen now shows the real reason and what to do ("download the update again"), and offers Download again instead of a dead "Install" button, rather than a generic "it failed".
|
||||
- **Video on the kiosk stops tearing.** The kiosk's display had no vertical sync at all, so fast motion — IndeedHub films especially — showed horizontal tearing lines. The display driver now syncs every frame to the panel (no extra hardware needed, existing kiosks pick it up with this update), and on machines with a GPU, video decoding moves off the CPU onto the video hardware — smoother playback that also leaves more headroom for audio, not less.
|
||||
- **The Back button finally does what you expect.** Pressing Back — the mouse's side button on a kiosk, a swipe on a phone, the toolbar button in any browser — used to navigate the screen underneath an open window, or leave the dashboard entirely. Back now closes the topmost open window first, one per press, exactly like a native app; closing a window yourself never leaves a phantom entry that makes you press Back twice.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
app:
|
||||
id: alby-hub
|
||||
name: Alby Hub
|
||||
version: 1.23.0
|
||||
description: Self-custodial Lightning wallet hub. Runs its own Lightning node on your Archipelago and connects your apps to it over Nostr Wallet Connect — one hub, every app pays through it.
|
||||
category: money
|
||||
|
||||
container:
|
||||
image: source.archipelago-foundation.org/lfg2025/alby-hub:v1.23.0
|
||||
pull_policy: if-not-present
|
||||
|
||||
dependencies:
|
||||
- storage: 1Gi
|
||||
|
||||
resources:
|
||||
cpu_limit: 1
|
||||
memory_limit: 512Mi
|
||||
disk_limit: 2Gi
|
||||
|
||||
security:
|
||||
capabilities: []
|
||||
readonly_root: true
|
||||
no_new_privileges: true
|
||||
network_policy: bridge
|
||||
|
||||
ports:
|
||||
- host: 8187
|
||||
container: 8080
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /var/lib/archipelago/alby-hub
|
||||
target: /data
|
||||
options: [rw]
|
||||
|
||||
environment:
|
||||
- WORK_DIR=/data
|
||||
- PORT=8080
|
||||
# LDK peers are dialed outbound-only in v1; no inbound p2p port is
|
||||
# advertised, so no extra port mapping is needed for payments to work.
|
||||
- LOG_LEVEL=info
|
||||
|
||||
health_check:
|
||||
type: http
|
||||
endpoint: http://localhost:8080
|
||||
path: /
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
interfaces:
|
||||
main:
|
||||
name: Web UI
|
||||
description: Alby Hub wallet interface
|
||||
type: ui
|
||||
port: 8187
|
||||
protocol: http
|
||||
|
||||
metadata:
|
||||
icon: /assets/img/app-icons/alby-hub.svg
|
||||
repo: https://github.com/getAlby/hub
|
||||
tier: optional
|
||||
launch:
|
||||
# Embedded: the gate neutralizes Alby Hub's X-Frame-Options: DENY on
|
||||
# proxied responses. Nodes older than the gate fix show a blocked
|
||||
# frame — flip to true only if targeting such nodes.
|
||||
open_in_new_tab: false
|
||||
features:
|
||||
- Self-custodial Lightning node (LDK) with a friendly wallet UI
|
||||
- Connect wallets and apps via Nostr Wallet Connect (NWC)
|
||||
- Per-app budgets and isolated sub-wallets
|
||||
- Works with the Alby browser extension and mobile app
|
||||
@@ -0,0 +1,75 @@
|
||||
app:
|
||||
id: phoenixd
|
||||
name: phoenixd
|
||||
version: 0.9.0
|
||||
description: Headless Lightning daemon by ACINQ (the Phoenix wallet team). No screen of its own — it exposes a small local API that other apps and tools use to send and receive Lightning payments. Channel liquidity is managed automatically for a fee.
|
||||
category: money
|
||||
|
||||
container:
|
||||
# Image entrypoint already runs with --agree-to-terms-of-service and
|
||||
# --http-bind-ip 0.0.0.0, as user "phoenix"; no custom args needed.
|
||||
image: source.archipelago-foundation.org/lfg2025/phoenixd:0.9.0
|
||||
pull_policy: if-not-present
|
||||
# The image runs as user phoenix (1000:1000); the datadir bind source
|
||||
# must be chowned to that identity or phoenixd dies on
|
||||
# "Failed to open /data/phoenix.conf with Permission denied".
|
||||
data_uid: "1000:1000"
|
||||
|
||||
dependencies:
|
||||
- storage: 500Mi
|
||||
|
||||
resources:
|
||||
cpu_limit: 1
|
||||
memory_limit: 512Mi
|
||||
disk_limit: 1Gi
|
||||
|
||||
security:
|
||||
capabilities: []
|
||||
readonly_root: true
|
||||
no_new_privileges: true
|
||||
network_policy: bridge
|
||||
|
||||
ports:
|
||||
- host: 9740
|
||||
container: 9740
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
Loopback-only JSON API, not a web page. Every request is
|
||||
authenticated by the http password phoenixd generates in its own
|
||||
data directory on first run; the app gate's browser login page
|
||||
would break the API clients this port exists for.
|
||||
|
||||
volumes:
|
||||
# The wallet seed (seed.dat) and phoenix.conf live here. This directory
|
||||
# must survive reinstall/migration like any other app data dir —
|
||||
# losing it means losing funds.
|
||||
# Target is /data (via PHOENIX_DATADIR below), NOT the image's default
|
||||
# /phoenix/.phoenix: the orchestrator treats any bind path containing a
|
||||
# dot as a file mount and skips creating its source directory, so a
|
||||
# hidden-dir target never gets its host dir and the unit crash-loops.
|
||||
- type: bind
|
||||
source: /var/lib/archipelago/phoenixd
|
||||
target: /data
|
||||
options: [rw]
|
||||
|
||||
environment:
|
||||
- PHOENIX_DATADIR=/data
|
||||
|
||||
health_check:
|
||||
type: tcp
|
||||
endpoint: localhost:9740
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
metadata:
|
||||
icon: /assets/img/app-icons/phoenixd.svg
|
||||
repo: https://github.com/ACINQ/phoenixd
|
||||
tier: optional
|
||||
features:
|
||||
- Ultra-light Lightning node — no bitcoin node required
|
||||
- Automated channel and liquidity management (fees apply)
|
||||
- Simple HTTP API + websockets for payments
|
||||
- Backed by the team behind the Phoenix mobile wallet
|
||||
Generated
+1
-1
@@ -104,7 +104,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "archipelago"
|
||||
version = "1.7.129-alpha"
|
||||
version = "1.8.0-alpha"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"archipelago-container",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "archipelago"
|
||||
version = "1.7.129-alpha"
|
||||
version = "1.8.0-alpha"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
description = "Archipelago Bitcoin Node OS - Native backend"
|
||||
|
||||
@@ -520,11 +520,63 @@ async fn proxy_to_app(
|
||||
|
||||
let client = hyper::Client::new();
|
||||
match client.request(Request::from_parts(parts, body)).await {
|
||||
Ok(resp) => resp,
|
||||
Ok(mut resp) => {
|
||||
neutralize_frame_blocking(resp.headers_mut());
|
||||
resp
|
||||
}
|
||||
Err(_) => app_down_page(app),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make gate-proxied app responses embeddable by the dashboard's My Apps
|
||||
/// iframe. Apps that were never designed for framing ship
|
||||
/// `X-Frame-Options: DENY` (Alby Hub) or a CSP `frame-ancestors` directive,
|
||||
/// and either one makes the embedded app session a dead grey pane — the
|
||||
/// historical workaround was a bespoke per-app nginx proxy (gitea), which is
|
||||
/// exactly the per-app patching the manifest platform exists to delete.
|
||||
///
|
||||
/// Framing protection exists to stop a FOREIGN origin from framing an authed
|
||||
/// page and clickjacking it. Behind the gate that threat model is already
|
||||
/// handled the way the gate's own pages handle it: every proxied request is
|
||||
/// authenticated by the gate first, and the gate's own responses declare
|
||||
/// `frame-ancestors 'self' http://*:* https://*:*` (see `page()`) because the
|
||||
/// dashboard is reached by LAN IP, mDNS name, and onion alike. Upstream
|
||||
/// X-Frame-Options is dropped entirely; only the `frame-ancestors` directive
|
||||
/// is removed from the app's CSP — the rest of the app's policy (script-src,
|
||||
/// connect-src, …) is the app's business and passes through untouched.
|
||||
fn neutralize_frame_blocking(headers: &mut hyper::HeaderMap) {
|
||||
headers.remove("x-frame-options");
|
||||
let Some(csp) = headers.get("content-security-policy") else {
|
||||
return;
|
||||
};
|
||||
let Ok(raw) = csp.to_str() else {
|
||||
return;
|
||||
};
|
||||
if !raw.to_ascii_lowercase().contains("frame-ancestors") {
|
||||
return;
|
||||
}
|
||||
let kept: Vec<&str> = raw
|
||||
.split(';')
|
||||
.map(str::trim)
|
||||
.filter(|d| !d.to_ascii_lowercase().starts_with("frame-ancestors") && !d.is_empty())
|
||||
.collect();
|
||||
if kept.is_empty() {
|
||||
headers.remove("content-security-policy");
|
||||
return;
|
||||
}
|
||||
match header::HeaderValue::from_str(&kept.join("; ")) {
|
||||
Ok(v) => {
|
||||
headers.insert("content-security-policy", v);
|
||||
}
|
||||
Err(_) => {
|
||||
// Unrepresentable after filtering — fail open for framing but
|
||||
// closed for the policy: better to drop a mangled CSP than to
|
||||
// serve one we rewrote incorrectly.
|
||||
headers.remove("content-security-policy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cookie names owned by the gate/daemon, never the app's to see.
|
||||
const GATE_COOKIE_NAMES: &[&str] = &["session", "csrf_token"];
|
||||
|
||||
@@ -1072,6 +1124,50 @@ mod tests {
|
||||
/// 2026-08-05). It must still be uncacheable, and still refuse to be
|
||||
/// framed by a foreign origin, which `frame-ancestors` expresses and
|
||||
/// `X-Frame-Options` cannot.
|
||||
/// Upstream frame-blocking must not survive the proxy: X-Frame-Options
|
||||
/// goes away entirely, CSP loses ONLY its frame-ancestors directive —
|
||||
/// the app's remaining policy must pass through byte-preserving in
|
||||
/// content (Alby Hub's DENY + strict CSP was the real-world case,
|
||||
/// archi-dev-box 2026-08-12).
|
||||
#[test]
|
||||
fn proxied_responses_lose_frame_blocking_but_keep_the_apps_csp() {
|
||||
let mut headers = hyper::HeaderMap::new();
|
||||
headers.insert("x-frame-options", "DENY".parse().unwrap());
|
||||
headers.insert(
|
||||
"content-security-policy",
|
||||
"default-src 'self'; frame-ancestors 'none'; img-src 'self' https://cdn.example"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
neutralize_frame_blocking(&mut headers);
|
||||
assert!(!headers.contains_key("x-frame-options"));
|
||||
let csp = headers["content-security-policy"].to_str().unwrap();
|
||||
assert!(!csp.contains("frame-ancestors"));
|
||||
assert!(csp.contains("default-src 'self'"));
|
||||
assert!(csp.contains("img-src 'self' https://cdn.example"));
|
||||
|
||||
// CSP that is ONLY a frame-ancestors directive disappears entirely.
|
||||
let mut only = hyper::HeaderMap::new();
|
||||
only.insert(
|
||||
"content-security-policy",
|
||||
"frame-ancestors 'self'".parse().unwrap(),
|
||||
);
|
||||
neutralize_frame_blocking(&mut only);
|
||||
assert!(!only.contains_key("content-security-policy"));
|
||||
|
||||
// No frame directives at all → CSP untouched.
|
||||
let mut plain = hyper::HeaderMap::new();
|
||||
plain.insert(
|
||||
"content-security-policy",
|
||||
"default-src 'self'".parse().unwrap(),
|
||||
);
|
||||
neutralize_frame_blocking(&mut plain);
|
||||
assert_eq!(
|
||||
plain["content-security-policy"].to_str().unwrap(),
|
||||
"default-src 'self'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn challenge_pages_are_uncacheable_and_framable_only_by_this_node() {
|
||||
let resp = login_page(&app(), None, StatusCode::UNAUTHORIZED);
|
||||
|
||||
@@ -1540,9 +1540,7 @@ pub async fn apply_update(data_dir: &Path) -> Result<()> {
|
||||
.await?
|
||||
.available_update
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Update manifest missing from state — re-download the update"
|
||||
)
|
||||
anyhow::anyhow!("Update manifest missing from state — re-download the update")
|
||||
})?;
|
||||
verify_staged_components(&staging_dir, &manifest).await?;
|
||||
|
||||
|
||||
+98
-13
@@ -130,7 +130,38 @@ app:
|
||||
|
||||
Additional extension keys may exist for current integrations, for example Bitcoin, Lightning, or app-specific launch/interface metadata. Treat extension keys as transitional unless they are documented as reusable platform primitives.
|
||||
|
||||
Use `metadata.launch.open_in_new_tab: true` when the app UI is known to reject iframe embedding with headers such as `X-Frame-Options` or restrictive CSP. The frontend app-session metadata is generated from this flag during release work.
|
||||
### Iframe embedding — the rules
|
||||
|
||||
The dashboard opens apps in an **embedded frame** (My Apps → app session) by
|
||||
default. Whether that works is decided by HTTP headers, not by wishes, so
|
||||
know the mechanics:
|
||||
|
||||
- Browsers refuse to render a page in an iframe when the response carries
|
||||
`X-Frame-Options: DENY`/`SAMEORIGIN` (the dashboard and the app are
|
||||
different origins — different port at minimum) or a CSP `frame-ancestors`
|
||||
directive that excludes the dashboard's origin.
|
||||
- Many upstream apps ship exactly those headers (Alby Hub sends
|
||||
`X-Frame-Options: DENY`). In a normal deployment that is correct hardening;
|
||||
behind Archipelago's app gate the clickjacking threat those headers address
|
||||
is already handled — every proxied request is authenticated by the gate
|
||||
first.
|
||||
- Therefore **the gate neutralizes frame blocking on proxied responses**: it
|
||||
removes `X-Frame-Options` and strips only the `frame-ancestors` directive
|
||||
from the app's CSP. The rest of the app's CSP (script-src, connect-src, …)
|
||||
passes through untouched — the gate never weakens the app's own content
|
||||
policy, only its framing policy. You do not need a bespoke reverse proxy,
|
||||
header patches, or app config to be embeddable.
|
||||
|
||||
Set `metadata.launch.open_in_new_tab: true` only when embedding is broken by
|
||||
things headers can't fix — the app frame-busts in JavaScript, requires being
|
||||
the top-level origin (OAuth redirect flows, WebAuthn), or sets
|
||||
`SameSite=Strict` session cookies that never accompany framed requests. Test
|
||||
in the real embedded app session, **not** a plain browser tab: tabs don't
|
||||
enforce framing headers, so a tab proves nothing about the iframe.
|
||||
|
||||
(Historical note: before the gate handled this, embeddable-but-blocking apps
|
||||
each carried a hand-built nginx strip proxy — gitea's port-3000 proxy is the
|
||||
surviving example. Do not copy that pattern for new apps.)
|
||||
|
||||
### Launch Interfaces
|
||||
|
||||
@@ -406,19 +437,73 @@ curl http://localhost:8180/health
|
||||
podman logs my-app
|
||||
```
|
||||
|
||||
### On an Archipelago Node
|
||||
### On an Archipelago Node (before your app is in the catalog)
|
||||
|
||||
1. Install via the marketplace UI or RPC:
|
||||
```bash
|
||||
curl -b cookies.txt -X POST http://archipelago.local/rpc/v1 \
|
||||
-d '{"method":"package.install","params":{"id":"my-app","dockerImage":"docker.io/myorg/my-app:1.0.0"}}'
|
||||
```
|
||||
2. Verify the container is running:
|
||||
```bash
|
||||
curl -b cookies.txt -X POST http://archipelago.local/rpc/v1 \
|
||||
-d '{"method":"container-list"}'
|
||||
```
|
||||
3. Check the UI. The app's detail page is `http://archipelago.local/dashboard/apps/my-app`; the embedded launch surface is `http://archipelago.local/dashboard/app-session/my-app`
|
||||
The App Store lists **signed-catalog apps and Nostr-discovered apps only** —
|
||||
a manifest on the node's disk never appears in the store by itself. That is
|
||||
deliberate: the store is a trust surface. But the orchestrator installs from
|
||||
disk manifests just fine, so you can test the complete install/run/uninstall
|
||||
lifecycle on your own node before your app is published anywhere.
|
||||
|
||||
**1. Stage the manifest where it survives reboots.**
|
||||
|
||||
`/opt/archipelago/apps/` is *rebuilt on every backend start* from the runtime
|
||||
payload that ships inside the frontend bundle
|
||||
(`/opt/archipelago/web-ui/archipelago-runtime/apps/`). If you copy your
|
||||
manifest only into `/opt/archipelago/apps/`, the next restart silently deletes
|
||||
it. Stage into the payload directory instead — the boot sync then promotes it
|
||||
for you:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/archipelago/web-ui/archipelago-runtime/apps/my-app
|
||||
sudo cp apps/my-app/manifest.yml /opt/archipelago/web-ui/archipelago-runtime/apps/my-app/
|
||||
sudo systemctl restart archipelago # manifests are loaded at startup
|
||||
```
|
||||
|
||||
Watch `journalctl -u archipelago` after the restart — the orchestrator
|
||||
validates every manifest on load and tells you about problems immediately
|
||||
(for example a host-port collision with another installed app).
|
||||
|
||||
**2. Install over JSON-RPC.**
|
||||
|
||||
The repo ships the same session helper the release lifecycle gate uses.
|
||||
Three things to know before using it: it needs `jq`; it reuses a cached
|
||||
session from `/tmp/archy-rpc-session-<uid>` unless `ARCHY_FORCE_LOGIN=1` is
|
||||
set (a stale cache fails every call quietly); and it sets `set -euo pipefail`,
|
||||
so run it inside a script or subshell — sourcing it into your interactive
|
||||
shell makes the first failed step kill the whole chain without printing
|
||||
anything.
|
||||
|
||||
```bash
|
||||
bash <<'EOF'
|
||||
export ARCHY_PASSWORD='<your dashboard password>' ARCHY_FORCE_LOGIN=1
|
||||
# Stock nodes serve HTTPS on 443; dev boxes behind plain nginx use:
|
||||
# export ARCHY_HOST=127.0.0.1 ARCHY_SCHEME=http
|
||||
source tests/lifecycle/lib/rpc.bash
|
||||
rpc_login && echo "login ok"
|
||||
# Both fields are required: `dockerImage` is normally supplied by the App
|
||||
# Store from the signed catalog — pre-catalog, you pass your manifest's
|
||||
# image yourself (it must match, and must come from a trusted registry).
|
||||
rpc_call package.install '{"id":"my-app","dockerImage":"docker.io/myorg/my-app:1.0.0"}'
|
||||
EOF
|
||||
```
|
||||
|
||||
**3. Verify the lifecycle, not just the install:**
|
||||
|
||||
```bash
|
||||
rpc_call package.status '{"id":"my-app"}' # state + health (run inside the same subshell pattern)
|
||||
podman ps --filter name=my-app # container is up
|
||||
rpc_call package.stop '{"id":"my-app"}' # …and start, restart
|
||||
sudo systemctl restart archipelago # app must survive this
|
||||
rpc_call package.uninstall '{"id":"my-app","preserve_data":true}'
|
||||
rpc_call package.install '{"id":"my-app"}' # data still there?
|
||||
```
|
||||
|
||||
The app's detail page is `https://<node>/dashboard/apps/my-app`; a gated web
|
||||
UI is reachable through the app gate on its manifest port once running.
|
||||
|
||||
Only after this loop is green does the app belong in a catalog submission —
|
||||
catalog inclusion is what makes it appear in the App Store.
|
||||
|
||||
### Validate Manifest
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "neode-ui",
|
||||
"version": "1.7.129-alpha",
|
||||
"version": "1.8.0-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "neode-ui",
|
||||
"version": "1.7.129-alpha",
|
||||
"version": "1.8.0-alpha",
|
||||
"dependencies": {
|
||||
"@scure/bip39": "^2.2.0",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "neode-ui",
|
||||
"private": true,
|
||||
"version": "1.7.129-alpha",
|
||||
"version": "1.8.0-alpha",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "./start-dev.sh",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8000 8000">
|
||||
<path d="M1404.94034,2404.75143c.19948.39893.416.79745.63896,1.19934-.21965-.3998-.4293-.7996-.63896-1.19934ZM1398.36987,2392.55825c.01.02001.01999.03996.01999.04996.01999.02001.03998.04996.04998.06997-.01999-.03996-.04998-.07997-.06997-.11993ZM7957.22848,6407.80899c-75.3022-453.82005-341.31246-1051.68618-631.76608-1536.83905-217.58172,280.41299-443.70125,543.12568-695.66263,796.78364,151.96083,272.22676,381.52012,831.33939,371.78586,1131.07073-3.34955,103.13742-84.59871,186.38404-187.48323,193.03587-323.69068,20.92769-761.15088-169.80941-1126.96417-352.01125-274.08918,250.36976-539.73074,478.97192-815.02811,690.20321,570.85548,338.56749,1305.54247,669.74202,1904.46573,668.08293,872.77622,46.26671,1378.68446-776.20339,1180.65263-1590.32607ZM5249.15884,5683.56479c1101.91285-1101.01171,2353.03599-2527.31198,2708.11113-4090.5697,253.93721-1273.80049-760.34667-1869.98142-1895.16006-1469.66567,211.65045,278.23428,364.22738,589.33979,457.73054,914.98744,86.80988-20.20216,196.93112-34.80914,289.99476-31.50156,103.33929,3.67279,186.93541,86.38156,191.60732,189.77353,13.96581,309.07109-235.73659,902.8025-402.1382,1193.8406,0,.01-.01.02001-.01.03001l-.04998.07991c-624.44753,1143.09071-1591.89389,2208.42396-2600.29743,3077.06633l-.22964-.19984c-263.64443,226.46298-526.16056,433.98687-804.2439,626.56893.39938.34966.79882.69956,1.19826,1.04946-509.31622,353.04862-1419.46673,925.33606-2012.13405,893.44541-103.82534-5.58671-186.23795-90.1864-188.67838-194.22674-6.97909-297.53161,215.6413-843.71786,363.83869-1111.11806-237.05325-259.94423-464.71032-529.69327-686.31643-814.38364-1228.33988,1900.32534-772.83666,3911.20697,1772.96127,2814.86279,1004.95316-454.63593,1968.00538-1208.05697,2803.81609-2000.0392ZM2528.2177,4000.38756c-468.17105-551.20508-806.89093-1020.88409-1125.16461-1599.13417h-.01c-.00197-.02709-.02111-.04517-.02999-.07997h-.01c-.00225-.03154-.04191-.05163-.03986-.08992,0,0-.01,0-.01-.01-1.46642-2.7001-3.08021-5.72568-4.5634-8.46529.01999.02001.03998.04996.04998.06997-165.01013-287.23702-415.64692-882.63022-403.53149-1193.46936,4.07951-104.66591,88.1281-188.31126,192.71288-192.04153,92.1143-3.28547,200.69379,10.46969,286.62767,30.61985,93.58312-325.69743,246.26999-636.85321,458.06014-915.09749C797.66386-275.84563-214.1867,319.25478,39.32792,1592.99819c208.75964,1126.24494,1140.55524,2354.14531,1865.30175,3206.20191-1.24817,1.76903,1.23818-1.76903,0,0,177.91975,202.67638,363.17881,406.52227,558.17368,604.08153,267.29912-191.50259,530.82373-396.10806,795.10723-617.7438-259.10108-257.46588-503.73334-520.6384-729.69288-785.15026ZM1420.59738,2433.86513c.84886,1.56913,1.71746,3.17821,2.5963,4.82725-.85885-1.58908-1.72745-3.19816-2.5963-4.82725ZM1457.40366,2502.26682c.92857,1.699,1.83716,3.39806,2.73599,5.07711-.89883-1.64904-1.80742-3.3481-2.73599-5.07711ZM1403.01311,2401.17342c-.01-.01-.01987-.02995-.02986-.03996.01.01.01987.02995.01987.03996.01999.03001.02999.04996.03998.06997-.01-.02001-.01999-.03996-.02999-.06997ZM1404.94034,2404.75143c.19948.39893.416.79745.63896,1.19934-.21965-.3998-.4293-.7996-.63896-1.19934Z" fill="#ffe480"/>
|
||||
<path d="M2762.56314,2987.43935c-683.41752-683.42045-683.41752-1791.46134,0-2474.87781,683.41752-683.41755,1791.45841-683.41755,2474.87885,0,683.41068,683.41648,683.41068,1791.45737,0,2474.87781l-1237.44089,1237.44089-1237.43796-1237.44089Z" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="0 0 94 94" id="vector" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="group" transform="matrix(1, 0, 0, 1, -6.999915, -7)">
|
||||
<path id="path" d="M 30 81 C 23 80 21.63 71.49 29.16 45.87 C 35 26 46.18 26 58 26 C 73.17 26 81.61 29.18 83.79 35.73 C 84.243 36.883 84.39 38.135 84.214 39.362 C 84.039 40.589 83.548 41.75 82.79 42.73 C 82.613 42.96 82.419 43.178 82.21 43.38 C 85.1 45.31 86.88 48.02 86.52 51.9 C 86.13 56.26 83.16 58.55 78.52 59.66 C 78.849 60.396 79.013 61.194 79 62 C 78.988 63.199 78.695 64.378 78.147 65.444 C 77.598 66.51 76.808 67.433 75.84 68.14 C 70.09 72.76 60 75 46 73 C 43 72.57 41 75 39 77 C 36.62 79.38 34.24 81.61 30 81 Z" fill="#25A5FF"/>
|
||||
<path id="path_1" d="M 58 30 C 66 30 78 31 80 37 C 82.24 43.71 69 43 59 42 C 68 43 83.18 44 82.5 51.5 C 82 57 72 57 55 56 C 63 57 75 58 75 62 C 75 68.28 58.74 71 47.67 69.31 C 39 68 38.06 76.77 31 75 C 27 74 28 63 33 47 C 38.29 30.09 46.49 30 58 30 Z" fill="#ffffff" stroke="#25A5FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,5 +1,39 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<!-- KAMMERGUT GLOSS TEST (2026-08-12): SVG paint filter lifted verbatim
|
||||
from plan-b's Kammergut wordmark (#paintGloss). Consumed by the
|
||||
.logo-gradient-border::after override in style.css. Revert by
|
||||
deleting this svg block + that css block (one commit). -->
|
||||
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
|
||||
<defs>
|
||||
<filter id="paintGloss" x="-12%" y="-30%" width="124%" height="160%" color-interpolation-filters="sRGB">
|
||||
<feMorphology in="SourceAlpha" operator="dilate" radius="2.5" result="dilated"/>
|
||||
<feGaussianBlur in="dilated" stdDeviation="6" result="puddle"/>
|
||||
<feFlood flood-color="#fff4dc" flood-opacity="0.85" result="puddleColor"/>
|
||||
<feComposite in="puddleColor" in2="puddle" operator="in" result="puddleHi"/>
|
||||
<feFlood flood-color="#1A1A18" flood-opacity="0.18" result="puddleShadowColor"/>
|
||||
<feComposite in="puddleShadowColor" in2="puddle" operator="in" result="puddleShadow"/>
|
||||
<feOffset in="puddleShadow" dx="0" dy="3" result="puddleShadowOff"/>
|
||||
<feGaussianBlur in="SourceAlpha" stdDeviation="1.4" result="bump"/>
|
||||
<feSpecularLighting in="bump" surfaceScale="4" specularConstant="0.9"
|
||||
specularExponent="40" lighting-color="#fff2d4" result="spec2">
|
||||
<fePointLight x="600" y="-200" z="380"/>
|
||||
</feSpecularLighting>
|
||||
<feComposite in="spec2" in2="SourceAlpha" operator="in" result="specClip2"/>
|
||||
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="dsBlur"/>
|
||||
<feOffset in="dsBlur" dx="0" dy="6" result="dsOff"/>
|
||||
<feComponentTransfer in="dsOff" result="dsFinal">
|
||||
<feFuncA type="linear" slope="0.45"/>
|
||||
</feComponentTransfer>
|
||||
<feMerge>
|
||||
<feMergeNode in="dsFinal"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
<feMergeNode in="specClip2"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<!-- Splash Screen (only on first visit) -->
|
||||
<SplashScreen v-if="showSplash" @complete="handleSplashComplete" />
|
||||
|
||||
|
||||
@@ -1128,6 +1128,16 @@ html.controller-nav [data-controller-container]:focus {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* KAMMERGUT GLOSS TEST (2026-08-12) — black gloss paint on the badge's
|
||||
inner circle, lifted from plan-b's Kammergut wordmark (.paint-3d
|
||||
gradient + #paintGloss filter, defs injected in App.vue). Revert:
|
||||
delete this block + the svg defs block in App.vue (one commit). */
|
||||
.logo-gradient-border::after {
|
||||
background: linear-gradient(180deg, #2a2a26 0%, #1a1a18 45%, #060604 100%);
|
||||
filter: url(#paintGloss);
|
||||
-webkit-filter: url(#paintGloss);
|
||||
}
|
||||
|
||||
.logo-gradient-border img,
|
||||
.logo-gradient-border svg {
|
||||
border-radius: 9999px;
|
||||
|
||||
@@ -362,6 +362,22 @@ init()
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-y-auto flex-1 min-h-0 space-y-6 pr-1">
|
||||
<!-- v1.8.0-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.0-alpha</span>
|
||||
<span class="text-xs text-white/40">August 12, 2026</span>
|
||||
</div>
|
||||
<div class="space-y-3 text-sm text-white/80 pl-3 border-l border-white/10">
|
||||
<p>**Archipelago is now open source.** The full source code of the node you are running — the orchestrator, the dashboard, the app platform, the mesh, the release tooling — is published for anyone to read, build and audit at source.archipelago-foundation.org/lfg2025/archy. A node that holds your money, your files and your communications should not ask to be taken on faith: from this release onward you, or anyone you trust, can see exactly what it does and follow every change we make in the open.</p>
|
||||
<p>**Installing an update is reliable again, and tells you what happened when it isn't.** Some nodes could download an update but never apply it — the button stayed on "Install", and no amount of retrying worked. The cause: applying the update consumed the downloaded files as it went, so if any one step hit a snag partway through, the leftover files were incomplete and every later attempt failed the safety re-check forever, needing a technician to recover. Applying no longer consumes the download — a failed apply can always be retried from the same files — and the pieces are now applied in a fixed order with the program itself last, so a hiccup can't leave a half-swapped node. When an apply does fail, the screen now shows the real reason and what to do ("download the update again"), and offers Download again instead of a dead "Install" button, rather than a generic "it failed".</p>
|
||||
<p>**Video on the kiosk stops tearing.** The kiosk's display had no vertical sync at all, so fast motion — IndeedHub films especially — showed horizontal tearing lines. The display driver now syncs every frame to the panel (no extra hardware needed, existing kiosks pick it up with this update), and on machines with a GPU, video decoding moves off the CPU onto the video hardware — smoother playback that also leaves more headroom for audio, not less.</p>
|
||||
<p>**The Back button finally does what you expect.** Pressing Back — the mouse's side button on a kiosk, a swipe on a phone, the toolbar button in any browser — used to navigate the screen underneath an open window, or leave the dashboard entirely. Back now closes the topmost open window first, one per press, exactly like a native app; closing a window yourself never leaves a phantom entry that makes you press Back twice.</p>
|
||||
<p>**No more bare IP addresses in your update or app-registry settings.** The update mirrors and the app registry each listed the same server twice — once by its proper name, once as a raw http://146… address left over from before the domain existed. The raw-address entries are retired: new nodes never see them, and existing nodes clean them out of their saved lists automatically on the next read. Everything now goes through the named, TLS-protected origin — which was always the same machine.</p>
|
||||
<p>**The phone companion app downloads over the proper domain.** The download QR pointed at a raw address over plain HTTP; it now points at the same file on the https domain. Scanning it gets you an encrypted download from a named server.</p>
|
||||
<p>**The Receive window now tells you when the money is on its way.** Previously it showed a QR code and left you to check elsewhere whether anything happened. Now, the moment the sender's transaction is broadcast, the QR gives way to a clock: the amount, the transaction ID (tap to copy), and a note that the funds arrive on their own — with a single Done button. If you keep the window open, the clock becomes a green check at the first confirmation. Verified live on a real node: payment detected within seconds of broadcast.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- v1.7.129-alpha -->
|
||||
<div>
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
|
||||
+21
-20
@@ -1,32 +1,33 @@
|
||||
{
|
||||
"changelog": [
|
||||
"**Every app is now supervised the same way — the last stragglers moved under systemd.** Five apps (Jellyfin, Nextcloud, Home Assistant, Uptime Kuma, Vaultwarden) still ran outside the node's per-app service management for a technical reason: their networking style died with whatever process started it, so they were kept alive by a separate workaround. That workaround is retired: these apps now migrate themselves onto the same managed units as everything else — own service, restart-on-anything, a ten-second breather between restarts so their networking can release its ports cleanly. The migration happens automatically on the node's next housekeeping pass, touches no app data, and was watched live on a real node: both test apps moved over on the first pass and came back healthy.",
|
||||
"**Leftover companion screens are cleaned up again — driven by real records this time.** When an app is uninstalled, its helper screen (the UI tile that fronts it) should go too. That cleanup was switched off in an earlier release after it wrongly removed the Bitcoin screen from a node whose Bitcoin was installed — it had been guessing \"installed\" from what happened to be running, and a separate bug made a running app look absent. The node now keeps a durable record of what you have installed, written at install time and cleared only by a real uninstall, and the cleanup consults only that record. If the record can't be read, the cleanup does nothing at all — \"I couldn't check\" is never treated as \"nothing is installed\" — and a helper must be orphaned for a sustained period before it is touched.",
|
||||
"**A warning that fired every minute on every node is gone.** The app catalog and the node disagreed about where Grafana's software comes from, so the node ignored the catalog's answer and logged a complaint roughly every 75 seconds, forever. The catalog was right — Grafana is served from the fleet's own registry, like Bitcoin Knots — and the node's records now agree with it.",
|
||||
"**The federation map became a real map.** The network view is now a 3D orbital scene of your federation — nodes as a point-cloud globe with calm motion, auto-fit centring, and a 2D top-down toggle that portrait and mobile screens use by default, with the scene filling the viewport instead of sitting in a letterbox. Inbound peer requests appear live on the map as blinking nodes you can accept or reject in place, and revisiting the view no longer replays the whole intro — the scene updates in place.",
|
||||
"**An app that's mid-restart shows a page that says so — and comes back by itself.** When an app's screen was briefly unreachable behind the gate, the browser got a bare error; it now gets a named page for that app that retries on its own until the app answers.",
|
||||
"Known gaps, disclosed rather than buried: three voice-assistant ports remain open without authentication. Non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. The 5x real-node lifecycle gate was not run for this release; the supervision migration and the cleanup re-enable were verified live on one node (both apps migrated and healthy, cleanup correctly idle)."
|
||||
"**Archipelago is now open source.** The full source code of the node you are running — the orchestrator, the dashboard, the app platform, the mesh, the release tooling — is published for anyone to read, build and audit at source.archipelago-foundation.org/lfg2025/archy. A node that holds your money, your files and your communications should not ask to be taken on faith: from this release onward you, or anyone you trust, can see exactly what it does and follow every change we make in the open.",
|
||||
"**Installing an update is reliable again, and tells you what happened when it isn't.** Some nodes could download an update but never apply it — the button stayed on \"Install\", and no amount of retrying worked. The cause: applying the update consumed the downloaded files as it went, so if any one step hit a snag partway through, the leftover files were incomplete and every later attempt failed the safety re-check forever, needing a technician to recover. Applying no longer consumes the download — a failed apply can always be retried from the same files — and the pieces are now applied in a fixed order with the program itself last, so a hiccup can't leave a half-swapped node. When an apply does fail, the screen now shows the real reason and what to do (\"download the update again\"), and offers Download again instead of a dead \"Install\" button, rather than a generic \"it failed\".",
|
||||
"**Video on the kiosk stops tearing.** The kiosk's display had no vertical sync at all, so fast motion — IndeedHub films especially — showed horizontal tearing lines. The display driver now syncs every frame to the panel (no extra hardware needed, existing kiosks pick it up with this update), and on machines with a GPU, video decoding moves off the CPU onto the video hardware — smoother playback that also leaves more headroom for audio, not less.",
|
||||
"**The Back button finally does what you expect.** Pressing Back — the mouse's side button on a kiosk, a swipe on a phone, the toolbar button in any browser — used to navigate the screen underneath an open window, or leave the dashboard entirely. Back now closes the topmost open window first, one per press, exactly like a native app; closing a window yourself never leaves a phantom entry that makes you press Back twice.",
|
||||
"**No more bare IP addresses in your update or app-registry settings.** The update mirrors and the app registry each listed the same server twice — once by its proper name, once as a raw `http://146…` address left over from before the domain existed. The raw-address entries are retired: new nodes never see them, and existing nodes clean them out of their saved lists automatically on the next read. Everything now goes through the named, TLS-protected origin — which was always the same machine.",
|
||||
"**The phone companion app downloads over the proper domain.** The download QR pointed at a raw address over plain HTTP; it now points at the same file on the https domain. Scanning it gets you an encrypted download from a named server.",
|
||||
"**The Receive window now tells you when the money is on its way.** Previously it showed a QR code and left you to check elsewhere whether anything happened. Now, the moment the sender's transaction is broadcast, the QR gives way to a clock: the amount, the transaction ID (tap to copy), and a note that the funds arrive on their own — with a single Done button. If you keep the window open, the clock becomes a green check at the first confirmation. Verified live on a real node: payment detected within seconds of broadcast."
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"current_version": "1.7.129-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.7.129-alpha/archipelago",
|
||||
"current_version": "1.8.0-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.0-alpha/archipelago",
|
||||
"name": "archipelago",
|
||||
"new_version": "1.7.129-alpha",
|
||||
"sha256": "675e7dafc855d59b38c5a12d8b9405894677ed8701580227beca95ec2912e3f2",
|
||||
"size_bytes": 59531400
|
||||
"new_version": "1.8.0-alpha",
|
||||
"sha256": "fd99219ea11ffebc92b83154e1058911ebe72c357c80085310c78aba9714a6b5",
|
||||
"size_bytes": 59369392
|
||||
},
|
||||
{
|
||||
"current_version": "1.7.129-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.7.129-alpha/archipelago-frontend-1.7.129-alpha.tar.gz",
|
||||
"name": "archipelago-frontend-1.7.129-alpha.tar.gz",
|
||||
"new_version": "1.7.129-alpha",
|
||||
"sha256": "53af2743308f4ae6a627255aaa288706d331d567c99e1cb615684dc3abba534d",
|
||||
"size_bytes": 95452033
|
||||
"current_version": "1.8.0-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.0-alpha/archipelago-frontend-1.8.0-alpha.tar.gz",
|
||||
"name": "archipelago-frontend-1.8.0-alpha.tar.gz",
|
||||
"new_version": "1.8.0-alpha",
|
||||
"sha256": "f6bbf7b3f78a00dd7051abef805c943cfa9a58f624e09e94b6bee6c9b2f22902",
|
||||
"size_bytes": 97608790
|
||||
}
|
||||
],
|
||||
"release_date": "2026-08-10",
|
||||
"signature": "902778710674486d5919e4abb1bf5540521c9ef55b50a44a9d64b750812738d51cc193f6675a9969b1c45ae86c2e9e44ab3d8daf8aa1d439799d7f7846e27d04",
|
||||
"release_date": "2026-08-12",
|
||||
"signature": "be3e3c6f8ed6b8d573a6329bf546ec1aa739214cd391272ce3b2f0a0ef6b10a4a4c4f9dfa4b9b32dad88326c9feff908fbdb3d1b86d055e5cf876f67f195c60b",
|
||||
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
|
||||
"version": "1.7.129-alpha"
|
||||
"version": "1.8.0-alpha"
|
||||
}
|
||||
|
||||
+21
-20
@@ -1,32 +1,33 @@
|
||||
{
|
||||
"changelog": [
|
||||
"**Every app is now supervised the same way — the last stragglers moved under systemd.** Five apps (Jellyfin, Nextcloud, Home Assistant, Uptime Kuma, Vaultwarden) still ran outside the node's per-app service management for a technical reason: their networking style died with whatever process started it, so they were kept alive by a separate workaround. That workaround is retired: these apps now migrate themselves onto the same managed units as everything else — own service, restart-on-anything, a ten-second breather between restarts so their networking can release its ports cleanly. The migration happens automatically on the node's next housekeeping pass, touches no app data, and was watched live on a real node: both test apps moved over on the first pass and came back healthy.",
|
||||
"**Leftover companion screens are cleaned up again — driven by real records this time.** When an app is uninstalled, its helper screen (the UI tile that fronts it) should go too. That cleanup was switched off in an earlier release after it wrongly removed the Bitcoin screen from a node whose Bitcoin was installed — it had been guessing \"installed\" from what happened to be running, and a separate bug made a running app look absent. The node now keeps a durable record of what you have installed, written at install time and cleared only by a real uninstall, and the cleanup consults only that record. If the record can't be read, the cleanup does nothing at all — \"I couldn't check\" is never treated as \"nothing is installed\" — and a helper must be orphaned for a sustained period before it is touched.",
|
||||
"**A warning that fired every minute on every node is gone.** The app catalog and the node disagreed about where Grafana's software comes from, so the node ignored the catalog's answer and logged a complaint roughly every 75 seconds, forever. The catalog was right — Grafana is served from the fleet's own registry, like Bitcoin Knots — and the node's records now agree with it.",
|
||||
"**The federation map became a real map.** The network view is now a 3D orbital scene of your federation — nodes as a point-cloud globe with calm motion, auto-fit centring, and a 2D top-down toggle that portrait and mobile screens use by default, with the scene filling the viewport instead of sitting in a letterbox. Inbound peer requests appear live on the map as blinking nodes you can accept or reject in place, and revisiting the view no longer replays the whole intro — the scene updates in place.",
|
||||
"**An app that's mid-restart shows a page that says so — and comes back by itself.** When an app's screen was briefly unreachable behind the gate, the browser got a bare error; it now gets a named page for that app that retries on its own until the app answers.",
|
||||
"Known gaps, disclosed rather than buried: three voice-assistant ports remain open without authentication. Non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. The 5x real-node lifecycle gate was not run for this release; the supervision migration and the cleanup re-enable were verified live on one node (both apps migrated and healthy, cleanup correctly idle)."
|
||||
"**Archipelago is now open source.** The full source code of the node you are running — the orchestrator, the dashboard, the app platform, the mesh, the release tooling — is published for anyone to read, build and audit at source.archipelago-foundation.org/lfg2025/archy. A node that holds your money, your files and your communications should not ask to be taken on faith: from this release onward you, or anyone you trust, can see exactly what it does and follow every change we make in the open.",
|
||||
"**Installing an update is reliable again, and tells you what happened when it isn't.** Some nodes could download an update but never apply it — the button stayed on \"Install\", and no amount of retrying worked. The cause: applying the update consumed the downloaded files as it went, so if any one step hit a snag partway through, the leftover files were incomplete and every later attempt failed the safety re-check forever, needing a technician to recover. Applying no longer consumes the download — a failed apply can always be retried from the same files — and the pieces are now applied in a fixed order with the program itself last, so a hiccup can't leave a half-swapped node. When an apply does fail, the screen now shows the real reason and what to do (\"download the update again\"), and offers Download again instead of a dead \"Install\" button, rather than a generic \"it failed\".",
|
||||
"**Video on the kiosk stops tearing.** The kiosk's display had no vertical sync at all, so fast motion — IndeedHub films especially — showed horizontal tearing lines. The display driver now syncs every frame to the panel (no extra hardware needed, existing kiosks pick it up with this update), and on machines with a GPU, video decoding moves off the CPU onto the video hardware — smoother playback that also leaves more headroom for audio, not less.",
|
||||
"**The Back button finally does what you expect.** Pressing Back — the mouse's side button on a kiosk, a swipe on a phone, the toolbar button in any browser — used to navigate the screen underneath an open window, or leave the dashboard entirely. Back now closes the topmost open window first, one per press, exactly like a native app; closing a window yourself never leaves a phantom entry that makes you press Back twice.",
|
||||
"**No more bare IP addresses in your update or app-registry settings.** The update mirrors and the app registry each listed the same server twice — once by its proper name, once as a raw `http://146…` address left over from before the domain existed. The raw-address entries are retired: new nodes never see them, and existing nodes clean them out of their saved lists automatically on the next read. Everything now goes through the named, TLS-protected origin — which was always the same machine.",
|
||||
"**The phone companion app downloads over the proper domain.** The download QR pointed at a raw address over plain HTTP; it now points at the same file on the https domain. Scanning it gets you an encrypted download from a named server.",
|
||||
"**The Receive window now tells you when the money is on its way.** Previously it showed a QR code and left you to check elsewhere whether anything happened. Now, the moment the sender's transaction is broadcast, the QR gives way to a clock: the amount, the transaction ID (tap to copy), and a note that the funds arrive on their own — with a single Done button. If you keep the window open, the clock becomes a green check at the first confirmation. Verified live on a real node: payment detected within seconds of broadcast."
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"current_version": "1.7.129-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.7.129-alpha/archipelago",
|
||||
"current_version": "1.8.0-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.0-alpha/archipelago",
|
||||
"name": "archipelago",
|
||||
"new_version": "1.7.129-alpha",
|
||||
"sha256": "675e7dafc855d59b38c5a12d8b9405894677ed8701580227beca95ec2912e3f2",
|
||||
"size_bytes": 59531400
|
||||
"new_version": "1.8.0-alpha",
|
||||
"sha256": "fd99219ea11ffebc92b83154e1058911ebe72c357c80085310c78aba9714a6b5",
|
||||
"size_bytes": 59369392
|
||||
},
|
||||
{
|
||||
"current_version": "1.7.129-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.7.129-alpha/archipelago-frontend-1.7.129-alpha.tar.gz",
|
||||
"name": "archipelago-frontend-1.7.129-alpha.tar.gz",
|
||||
"new_version": "1.7.129-alpha",
|
||||
"sha256": "53af2743308f4ae6a627255aaa288706d331d567c99e1cb615684dc3abba534d",
|
||||
"size_bytes": 95452033
|
||||
"current_version": "1.8.0-alpha",
|
||||
"download_url": "https://source.archipelago-foundation.org/lfg2025/archy/releases/download/v1.8.0-alpha/archipelago-frontend-1.8.0-alpha.tar.gz",
|
||||
"name": "archipelago-frontend-1.8.0-alpha.tar.gz",
|
||||
"new_version": "1.8.0-alpha",
|
||||
"sha256": "f6bbf7b3f78a00dd7051abef805c943cfa9a58f624e09e94b6bee6c9b2f22902",
|
||||
"size_bytes": 97608790
|
||||
}
|
||||
],
|
||||
"release_date": "2026-08-10",
|
||||
"signature": "902778710674486d5919e4abb1bf5540521c9ef55b50a44a9d64b750812738d51cc193f6675a9969b1c45ae86c2e9e44ab3d8daf8aa1d439799d7f7846e27d04",
|
||||
"release_date": "2026-08-12",
|
||||
"signature": "be3e3c6f8ed6b8d573a6329bf546ec1aa739214cd391272ce3b2f0a0ef6b10a4a4c4f9dfa4b9b32dad88326c9feff908fbdb3d1b86d055e5cf876f67f195c60b",
|
||||
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
|
||||
"version": "1.7.129-alpha"
|
||||
"version": "1.8.0-alpha"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user