umbrel/start9#874
Open
dskvr wants to merge 34 commits into
Open
Conversation
Two plans covering all 5 requirements (INFRA-01 through INFRA-07): - Plan 01 (Wave 1): Reproducible builds — pin Deno, replace esm.sh @latest, fix websocat arch, preserve deno.lock - Plan 02 (Wave 2): Unified s6-overlay image with process supervision, shutdown semantics, Umbrel compose Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace curl-based Deno install with COPY --from=denoland/deno:bin-2.7.5 in both Dockerfiles - Fix websocat download to support both x86_64 and aarch64 architectures - Remove deno.lock deletion from trawler Dockerfile - Remove silent failure fallbacks from deno cache commands - Update relaymon deno cache to remove --no-remote flag
…cifiers - Replace 15 esm.sh @latest imports in relaymon/deno.json with versioned npm: specifiers - Replace 22 esm.sh @latest + 1 npm:lmdb@latest imports in trawler/deno.json - Pin chalk to 5.6.2 (not 5.4.1/5.6.1 — supply chain safety) - Pin debug to 4.4.0 (not 4.4.1 — supply chain safety) - Regenerate deno.lock files for both apps using deno cache --reload - All versions sourced from existing deno.lock resolved versions
Mark Phase 14 Plan 01 as complete (1/2 plans done, in progress) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ess supervision - Unified single-container image based on debian:bookworm-slim - s6-overlay v3.2.2.0 installed as ENTRYPOINT ["/init"] (PID 1) - 4 longrun services: relaymon, trawler, hedproxy, config-ui placeholder - relaymon and trawler finish scripts run PRAGMA wal_checkpoint(TRUNCATE) for SQLite WAL safety - S6_KILL_GRACETIME=10000 allows 10s for clean shutdown - relaymon/dependencies.d/hedproxy enforces dependency ordering - Deno pinned to 2.7.5 via COPY --from=denoland/deno:bin pattern - hedproxy built from source; websocat installed with aarch64 detection Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ymon and trawler services - Multi-container compose using APP_DATA_DIR convention for persistent volumes - relaymon and trawler reference their respective (Plan-01-fixed) Dockerfiles - stop_grace_period: 15s on both services for SQLite WAL safety - RELAYMON_SKIP_PID_CHECK=true set for Docker environment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…man-verify checkpoint - SUMMARY.md: 2/3 tasks complete, Task 3 checkpoint pending - STATE.md: decisions recorded (s6-overlay chosen, dependency ordering, WAL grace time) - ROADMAP.md: Phase 14 plans updated (2 SUMMARY files present) - REQUIREMENTS.md: INFRA-06 and INFRA-07 marked complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Docker's COPY --from does not support ARG variable expansion. Use a named stage (FROM denoland/deno:bin-X AS deno-bin) and reference the stage name instead. Fixes all 3 Dockerfiles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The unified Start9 Dockerfile needs access to: - apps/trawler/ (bundled in single container) - apps/docker-stacks/start9/s6-services/ (service definitions) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Trawler imports nostrawl via relative path in deno.json import map. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… Dockerfile nostrawl library uses extensionless imports requiring sloppy-imports flag. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update SUMMARY.md with verified checkpoint results, 4 auto-fix deviations, and complete task history - Update STATE.md: decisions, metrics, session info - Update ROADMAP.md: Phase 14 marked complete (2/2 plans) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace single docker job with build-amd64, build-arm64, merge jobs - arm64 job uses native ubuntu-24.04-arm runner (no QEMU) - Both build jobs push by digest to Docker Hub and GHCR - arm64 job verifies deno --version && uname -m inside built image - Merge job creates multi-arch manifests with version+latest tags on both registries - GHA cache scoped per arch (relaymon-amd64, relaymon-arm64)
- Replace single docker job with build-amd64, build-arm64, merge jobs - arm64 job uses native ubuntu-24.04-arm runner (no QEMU) - Both build jobs push by digest to Docker Hub and GHCR - arm64 job verifies deno --version && uname -m inside built image - Merge job creates multi-arch manifests with version+latest tags on both registries - GHA cache scoped per arch (trawler-amd64, trawler-arm64)
- New workflow builds unified trawlmon image from apps/docker-stacks/start9/Dockerfile - Follows same 3-job split-runner pattern as relaymon and trawler workflows - arm64 job uses native ubuntu-24.04-arm runner (no QEMU) - Both build jobs push by digest to Docker Hub (nostrwatch/trawlmon) and GHCR - arm64 verification checks deno + uname -m + file /usr/local/bin/hedproxy (INFRA-05) - Triggers on trawlmon-v* tags; version sourced from apps/relaymon/deno.json - Cache scoped per arch (trawlmon-amd64, trawlmon-arm64)
- 15-01-SUMMARY.md: documents split-job pattern, 3 workflows, INFRA-04/05 completion - STATE.md: advance progress, record metrics, add 3 key decisions - ROADMAP.md: mark phase 15 plan 01 complete - REQUIREMENTS.md: mark INFRA-04 and INFRA-05 complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tests for atomic write (create tmp, rename, no tmp remains) - Tests for YAML validation (valid passes, invalid returns 400) - Tests for GET /api/config (200 with yaml, 404 when missing) - Tests for CORS headers (OPTIONS 204, JSON responses include ACAO) - Tests for proxy 503 when upstream unreachable
- createHandler() factory exports testable handler without starting server - GET /api/config returns raw YAML text (preserves comments), 404 on missing - PUT /api/config validates YAML via @std/yaml, atomic write via tmp+rename - GET /api/healthz and /api/metrics proxy to RELAYMON_HEALTH_URL, 503 on error - OPTIONS returns CORS headers; all JSON responses include ACAO: * - Static file serving with SPA fallback for paths without extensions - Add @std/http, @std/yaml, @std/path imports and config-ui/test:config-ui tasks to deno.json
- Created apps/relaymon/ui/ with package.json (@nostrwatch/config-ui) - Svelte 5 with runes mode, Vite 6, Tailwind 3 (darkMode: class) - Custom nostr-watch color scheme (primary purple #7C3AED, accent blue #3B82F6) - FOUC-prevention inline script in index.html for dark mode - Build produces single JS bundle + CSS at dist/assets/ (no hash in filenames) - npm install and vite build verified passing
RED step: validate.ts does not exist yet - all tests fail as expected. Tests cover validateRelayUrl, validateNpub, validateNsec, validateYaml, and validateConfig behaviors.
- Add 16-02-SUMMARY.md with decisions and metrics - Update STATE.md with position, decisions, session info - Update ROADMAP.md phase 16 progress (1/7 plans complete) - Mark requirements CFG-01, CFG-13 complete
…eme module - types.ts: standalone copies of HealthSnapshot, HealthState, Config and all sub-interfaces from relaymon source (browser-safe, no relaymon imports) plus UI-specific ConfigMode, SeedingMode, HealthzResponse types - validate.ts: validateRelayUrl, validateNpub, validateNsec, validateYaml, validateConfig — all 19 tests passing - api.ts: typed fetch wrappers for /api/config (GET/PUT), /api/healthz, /api/metrics - theme.svelte.ts: Svelte 5 $state runes with initTheme/toggleTheme, localStorage persistence, system preference fallback, FOUC-safe
- 16-01-SUMMARY.md: plan execution results, deviations, decisions - STATE.md: position updated, decisions logged, metrics recorded - ROADMAP.md: phase 16 plan progress updated - REQUIREMENTS.md: CFG-14 marked complete
- $state configState object: data, rawYaml, dirty, mode, loading, saveError, validationErrors, isFirstRun - loadConfig(): fetches from API, sets isFirstRun on 404, parses YAML to Config object - saveCurrentConfig(): serializes data or rawYaml based on mode, saves to API - setConfigFromYaml(): updates rawYaml + data (or just rawYaml on parse error) - setConfigFromObject(): stringifies Config and syncs rawYaml - setMode(): bidirectional raw<->object sync before switching, persists to localStorage
- StatsBar.svelte: sticky top bar with up/degraded/down indicator, uptime formatting (Xd Xh Xm), polls /api/healthz every 30s via onMount/onDestroy - StatsCards.svelte: 3-column responsive grid with Relays (total/online/offline), Queue (pending + progress bar), Publishing (success rate + pending) cards; polls /api/metrics every 30s - ThemeToggle.svelte: sun/moon inline SVG button, aria-label, uses theme.svelte.ts - App.svelte: composes ThemeToggle, StatsBar, StatsCards; calls initTheme() and loadConfig() in onMount; purple gradient heading; config form placeholder - All components handle unreachable state gracefully with -- placeholders
- SeedingSelector: three toggle cards (My Relays/Relay Lists/Network Scale) with mutual exclusion - Network Scale is mutually exclusive with My Relays and Relay Lists (and vice versa) - My Relays + Relay Lists can be active simultaneously - Clicking a card deactivates the excluded source but preserves its options - RelayUrlList: dynamic add/remove relay URL list with wss:// inline validation - Duplicate detection, Enter key support, accessible labels and role=alert errors - Both components read/write configState.data reactively via Svelte 5 $state
- SimpleMode: 9-section form (seeding, relay URLs, NIP-65, network scale, nsec, identity, publisher relays, networks, save) - Conditional sections appear/hide based on active seeding mode - nsec field: masked password input with show/hide toggle, top-level YAML key in save output - Monitor identity: name, description, slug inputs - Network modes: clearnet/Tor/I2P checkboxes with clearnet protection (cannot remove last) - Save button: validates config before save, loading spinner, success toast (3s auto-clear), error display - ConfigForm: three-tab mode router (Simple/Advanced/YAML) with purple underline indicator - Tab switching calls setMode() which preserves all data via bidirectional raw<->object sync - App.svelte: replaced placeholder with ConfigForm component - configState: added nsec field, saveSuccess flag, nsec included in YAML output on save - RelayUrlList: added onchange callback prop alongside $bindable for nested-state use cases
…ter plan - 16-04-SUMMARY.md: documents SeedingSelector, RelayUrlList, SimpleMode, ConfigForm components - STATE.md: updated decisions, session, progress (90%), metrics - ROADMAP.md: updated phase 16 plan progress (4/7 summaries) - REQUIREMENTS.md: marked CFG-02, CFG-03, CFG-06-12, CFG-15 complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Collapsible accordion sections for all Config fields - Seeding section: SeedingSelector + interval + source-specific options - Monitor Identity: all fields including owner, nip05, lud16, picture, banner, geo - Publisher: relay list + retry maxRetries + initialBackoffMs - Announce: frequency, userMetaRelays, nip66Relays - Checks: enabled checkboxes, options, per-check timeouts - Retry/Backoff: dynamic add/remove tier list - Network Modes: clearnet/tor/i2p/lokinet (adds lokinet vs SimpleMode) - Database: path + WAL toggle - Queue: worker concurrency (auto or number) - Log Level: dropdown - Ignore List: enabled, intervals, relay URLs, pubkeys - Delta Events: enabled, max_retries, periods - Health: server, Kuma, thresholds - Signing Key: same masked nsec pattern as SimpleMode - Timestring inputs accept format like 30s/5m/1h/1d - All fields bind reactively to configState.data
…er routing - Install svelte-codemirror-editor, @codemirror/lang-yaml, @codemirror/theme-one-dark - RawYamlMode: CodeMirror editor with YAML syntax highlighting and line numbers - Dark mode: oneDark theme applied reactively from theme.svelte.ts - Debounced (300ms) YAML parsing via setConfigFromYaml on each keystroke - Validation status bar: green Valid YAML or red errors below editor - Save button disabled when YAML is invalid - Raw text pass-through on save preserves YAML comments - ConfigForm: replace Advanced/Raw placeholders with AdvancedMode and RawYamlMode - Dirty indicator (amber dot + Unsaved text) visible in tab row - All three modes (Simple/Advanced/Raw) share configState for data preservation
…ting - Add Wizard.svelte: 5-step wizard (Welcome, Seeding, Sources, Key, Review) - Step 0 welcome screen with branding and feature highlights - Step 1 reuses SeedingSelector for seeding mode selection - Step 2 conditionally shows My Relays, npub, and/or Relay Lists relay inputs - Step 3 validates nsec with show/hide toggle, requires monitor name - Step 4 shows configuration summary and calls saveCurrentConfig() - App.svelte conditionally renders Wizard when configState.isFirstRun and no wizard_complete localStorage flag - After successful save, wizard disappears and normal dashboard renders - Back navigation works on all steps 1-4 - Progress bar and step indicator track wizard position
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create src/config/watcher.ts: Deno.watchFs-based config watcher - Watches for both modify and create events (atomic rename support) - 500ms debounce prevents double-firing from inotify rename pattern - Invalid config on reload logs warning, keeps current config (no crash) - Wire startConfigWatcher into main.ts after initial config load - Stop watcher on SIGTERM/SIGINT via stopConfigWatcher() in shutdown() - Add test:config-watcher task to deno.json
- Replace sleep infinity placeholder in s6 config-ui/run with Deno server - exec ensures Deno process is the s6-supervised process (correct PID) - Add config-ui service to Umbrel docker-compose.yaml on port 3000 - config-ui depends_on relaymon; uses Docker service name for health URL - Add RELAYMON_HEALTH_HOST=0.0.0.0 to relaymon so config-ui can reach it
ffce26c to
294cd9f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.