The restricted-RPC port (18090) was `auth: none`, which the app gate treats as fully exempt — no onion, no takeover, LAN/Tailscale IP only. Flip it to `auth: open`: the gate still binds the external addresses and fronts a Tor onion for the port, just without a dashboard login challenge, since Monero wallet clients (Feather, monero-wallet-rpc, GUI) speak plain HTTP JSON-RPC and can't hold a session cookie. P2P (18183) stays `none` — no reason to Tor-front raw gossip. Regenerated releases/app-catalog.json (unsigned) to embed the updated manifest; needs scripts/sign-catalog.sh before it takes effect on any node, since origin (catalog) wins over disk for catalog-covered apps. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZnsiMtyJxiJBuvv7yLPUF
194 lines
9.7 KiB
YAML
194 lines
9.7 KiB
YAML
app:
|
|
id: cuprate
|
|
name: Cuprate
|
|
# Matches the crate's own Cargo.toml version (binaries/cuprated/Cargo.toml).
|
|
# Cuprate has no stable release yet — this is explicitly work-in-progress
|
|
# software (see upstream README). The image tag below pins the exact
|
|
# commit built, since "0.1.0-preview" alone is not reproducible.
|
|
version: 0.1.0-preview
|
|
# Where this app comes from, so scripts/check-upstream-releases.py can
|
|
# tell us when the pin below has fallen behind. Without it nothing can:
|
|
# container.image names our mirror, not the project it was mirrored from.
|
|
upstream:
|
|
kind: github
|
|
repo: Cuprate/cuprate
|
|
description: Alternative Monero node implementation in Rust. Independently validates Monero consensus rules, providing a layer of security and redundancy for the network.
|
|
category: money
|
|
|
|
container:
|
|
# Built from the upstream Dockerfile at the tip of main, 18 commits past
|
|
# the cuprated-0.1.0-preview tag (commit 618ff14, 2026-08-19) — there is
|
|
# no newer tagged release as of this writing. Re-pin to a tagged release
|
|
# once upstream cuts one.
|
|
image: source.archipelago-foundation.org/lfg2025/cuprate:0.1.0-preview-18-g618ff14
|
|
pull_policy: if-not-present
|
|
network: archy-net
|
|
# The image's own ENTRYPOINT is ["/usr/local/bin/cuprated"]; these are
|
|
# appended as its argv, matching the project's own systemd unit
|
|
# (cuprated.service) invocation exactly.
|
|
custom_args: ["--config-file", "/home/cuprate/Cuprated.toml"]
|
|
# The image (FROM scratch) creates uid:gid 1000:1000 for the `cuprate`
|
|
# user at build time and runs as it unconditionally (USER 1000:1000,
|
|
# no shell to switch users at runtime) — same pattern as
|
|
# apps/phoenixd, apps/electrumx, apps/nostr-rs-relay, apps/portainer,
|
|
# apps/barkd. The bind-mounted data dir must be owned by that literal
|
|
# uid or cuprated dies on a permission error the first time it writes.
|
|
data_uid: "1000:1000"
|
|
|
|
dependencies:
|
|
# Monero mainnet is ~250GiB unpruned as of 2026 and growing a few GB a
|
|
# month; cuprated's pruning support is not confirmed stable yet (the
|
|
# `pruning` crate exists in the workspace but nothing in this config
|
|
# surface toggles it), so this sizes for a full unpruned chain plus
|
|
# headroom rather than assuming pruning is available.
|
|
- storage: 300Gi
|
|
|
|
resources:
|
|
cpu_limit: 0
|
|
# Raised from 4Gi alongside target_max_memory below (see files[] comment)
|
|
# — 2026-09-03 incident: a 4Gi/3GB-cache config starved
|
|
# cuprated's DB cache into constant eviction/flush, driving 45% sustained
|
|
# CPU and ~595GB/24h of block I/O on a fully-synced node. 10Gi leaves
|
|
# headroom above the 8GiB cache for the process itself.
|
|
memory_limit: 10Gi
|
|
disk_limit: 300Gi
|
|
|
|
security:
|
|
# FROM scratch, no package manager/shell, ownership fixed at build time
|
|
# — unlike bitcoin-knots this needs no runtime chown/setuid dance, so it
|
|
# can run fully read-only with an empty capability set.
|
|
capabilities: []
|
|
readonly_root: true
|
|
no_new_privileges: true
|
|
network_policy: isolated
|
|
|
|
ports:
|
|
# P2P. Cuprate's own default listen address is already 0.0.0.0
|
|
# (p2p.clear_net.listen_on), so no config override is needed — only the
|
|
# host-side port differs from Monero's canonical 18080 because that
|
|
# number is already taken on this fleet by lnd's REST port.
|
|
- host: 18183
|
|
container: 18080
|
|
protocol: tcp
|
|
auth: none
|
|
auth_rationale: >-
|
|
Monero p2p gossip. Peers are anonymous by design and speak the Monero wire protocol, not HTTP.
|
|
# Unrestricted RPC (full node control) is deliberately NOT published.
|
|
# cuprated has no RPC authentication, and for a published port to reach
|
|
# it the service would have to bind 0.0.0.0 inside the container — at
|
|
# which point every other app can reach it directly on 18081, since
|
|
# ports[].bind only restricts the HOST side and podman bridges route to
|
|
# each other (verified live 2026-08-22: a peer container on archy-net
|
|
# got an unauthenticated get_info, from a *different* network). That is
|
|
# unlike bitcoin-knots, whose 0.0.0.0 RPC still demands the rpcuser /
|
|
# rpcpassword it writes from generated secrets. So unrestricted RPC is
|
|
# left at cuprated's own default — container loopback only, reachable by
|
|
# nothing — which is also what upstream intends by refusing a non-local
|
|
# bind without an explicit i_know_what_im_doing override.
|
|
# Restricted RPC: Monero's own purpose-built safe-for-public subset —
|
|
# what wallets use when connecting to a "remote node". Disabled by
|
|
# cuprated's own default; enabled via files[] below. `open`, not `gated`:
|
|
# the gate still takes the port over (loopback pin, external binds,
|
|
# fronts the Tor onion) but skips the dashboard login challenge, same
|
|
# reasoning as electrumx's port — wallet clients (Feather,
|
|
# monero-wallet-rpc, GUI) speak plain HTTP JSON-RPC programmatically and
|
|
# cannot complete a browser login or hold a session cookie. The daemon
|
|
# still uses its canonical container port 18089, but Penpot already owns
|
|
# host port 18089, so this maps the public host port to the free 18090
|
|
# instead.
|
|
- host: 18090
|
|
container: 18089
|
|
protocol: tcp
|
|
auth: open
|
|
auth_rationale: >-
|
|
Monero restricted RPC — the subset upstream considers safe for public/remote-node use. Wallets (Feather, monero-wallet-rpc, GUI) connect directly over plain HTTP JSON-RPC and cannot complete a browser login or hold a dashboard session cookie.
|
|
|
|
volumes:
|
|
- type: bind
|
|
source: /var/lib/archipelago/cuprate
|
|
target: /home/cuprate
|
|
options: [rw]
|
|
|
|
# Settings that need to differ from cuprated's own documented defaults
|
|
# (verified against `cuprated --generate-config` and `--dry-run` locally,
|
|
# 2026-08-21):
|
|
# - fast_sync: cuprated's own default is false, which performs full
|
|
# cryptographic verification (ring signatures + RandomX PoW) on every
|
|
# incoming block instead of trusting checkpointed history. Root-caused
|
|
# 2026-09-03 as the dominant cause of a sustained 45% CPU node,
|
|
# vs. 2.8% on a reference node with fast_sync = true — same chain height, same
|
|
# block rate. Set explicitly rather than relying on the binary
|
|
# default so fresh deploys don't silently regress into full-verify.
|
|
# - target_max_memory: cuprated's own default auto-detects total *host*
|
|
# RAM via sysinfo, which inside a memory-limited container would let
|
|
# it size caches far past what resources.memory_limit above actually
|
|
# grants — same class of problem bitcoin-knots' -dbcache sizing
|
|
# comment addresses. Set explicitly, comfortably under the 10Gi limit.
|
|
# Previously 3000000000 (~2.8GiB); that starved the DB cache and
|
|
# forced constant eviction/flush (595GB/24h block I/O on a node just
|
|
# appending ~2MB blocks every 2 minutes) — raised to 8GiB, matching
|
|
# the healthy reference node, and
|
|
# resources.memory_limit above raised in step to keep headroom above it.
|
|
# - rpc.restricted.enable: cuprated ships this off by default; flip on
|
|
# so the auth:none host port above actually serves something instead
|
|
# of refusing every connection. port stays at its documented default
|
|
# (canonical 18089), and advertise stays false — this node is not
|
|
# opting in to being listed as a public remote node over the p2p
|
|
# network, just reachable if someone points a wallet at it directly.
|
|
# - rpc.unrestricted.address + the allow-public flag: cuprated's own
|
|
# default (127.0.0.1) looks like the obviously-correct choice for a
|
|
# port meant to stay loopback-only, but verified live (2026-08-21)
|
|
# that a service bound literally to 127.0.0.1 *inside* the container
|
|
# is unreachable through the host's published port — connections
|
|
# reset regardless of how long the daemon has been up. Binding
|
|
# 0.0.0.0 inside and letting ports[].bind: 127.0.0.1 below be the
|
|
# actual restriction is the same pattern apps/bitcoin-knots already
|
|
# uses for its own RPC port (-rpcbind=0.0.0.0:8332 internally, gate
|
|
# restricts it externally) — not a new risk, the same one already
|
|
# reviewed and accepted for Bitcoin's RPC.
|
|
# - tracing.stdout.level / tracing.file.{level,max_log_files}: an
|
|
# operator reading Cuprated.toml on disk should be able to see and
|
|
# tune the log level directly instead of the file silently omitting
|
|
# the whole [tracing] table (verified live on the affected node
|
|
# 2026-09-01: the deployed file had no [tracing] section at all, and
|
|
# the level was only discoverable by running `cuprated
|
|
# --generate-config` and diffing). file.level is set to "info", NOT
|
|
# cuprated's own raw default of "debug" — matches the reference dev
|
|
# config this app was built and tested against (verified 2026-09-01),
|
|
# which deliberately runs file logging quieter
|
|
# than the binary default. max_log_files similarly follows that
|
|
# reference (14, not the binary default of 7).
|
|
files:
|
|
- path: /var/lib/archipelago/cuprate/Cuprated.toml
|
|
content: |
|
|
network = "Mainnet"
|
|
fast_sync = true
|
|
target_max_memory = 8589934592
|
|
|
|
[rpc.restricted]
|
|
enable = true
|
|
|
|
[tracing.stdout]
|
|
level = "info"
|
|
|
|
[tracing.file]
|
|
level = "info"
|
|
max_log_files = 14
|
|
overwrite: false
|
|
|
|
health_check:
|
|
type: tcp
|
|
# Restricted RPC — the only RPC surface published now.
|
|
endpoint: localhost:18090
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 5m
|
|
|
|
metadata:
|
|
icon: /assets/img/app-icons/cuprate.svg
|
|
category: money
|
|
tier: optional
|
|
author: Cuprate
|
|
repo: https://github.com/Cuprate/cuprate
|