Docker-based Jellyfin media server with UCI config (port, image, media paths, GPU transcoding), procd init, jellyfinctl CLI, and LuCI frontend with status/config/logs view. Also adds Punk Exposure Engine architectural README documenting the Peek/Poke/Emancipate service exposure model and DNS provider API roadmap. CLAUDE.md updated with architectural directive. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.8 KiB
Punk Exposure Engine
Vision
Every SecuBox node is a generative station — it discovers what runs locally, and offers a unified flow to make any service reachable through all available channels: Tor .onion, classical DNS/HTTPS, P2P mesh, or all three at once.
Three verbs define the workflow:
- Peek — Scan, discover, look around. What's running? What's exposed? What domains are mapped? What peers are online?
- Poke — Target a service. Pick the exposure channels. Configure the linking flow.
- Emancipate — Activate. The service becomes reachable. DNS records are created, certificates are issued, .onion addresses are generated, mesh peers are notified.
Architecture
EMANCIPATE
|
+----------------+----------------+
| | |
Tor Layer DNS/SSL Layer Mesh Layer
(.onion) (HTTPS+ACME) (P2P peers)
| | |
tor-shield haproxyctl secubox-p2p
hidden svc + dns-provider-api + gossip sync
+ acme.sh
|
DNS Provider APIs
(OVH, Gandi, Cloudflare)
|
A/AAAA/CNAME records
created programmatically
Components
Existing (already built)
| Component | Package | What it does |
|---|---|---|
| Service scanner | secubox-app-exposure |
netstat scan enriched with UCI/Docker/process names |
| Tor exposure | secubox-app-tor + secubox-app-exposure |
tor_add() creates hidden service dir + torrc entry |
| SSL/HAProxy exposure | secubox-app-haproxy + secubox-app-exposure |
ssl_add() creates HAProxy backend + vhost + ACME cert |
| ACME certificates | secubox-app-haproxy |
acme.sh with HTTP-01 webroot validation via port 8402 |
| VHost manager | luci-app-vhost-manager |
Nginx-based vhost CRUD with ACME + templates |
| P2P mesh | secubox-p2p |
mDNS discovery, WireGuard mesh, service registry, gossip chain |
| Master-Link | secubox-master-link |
Hierarchical node onboarding with HMAC tokens + blockchain audit |
| Service registry | luci-app-service-registry |
Aggregates services across mesh, health checks, landing page |
| Exposure dashboard | luci-app-exposure |
Single-table KISS view: scan + Tor/SSL toggles per service |
Missing (to build)
| Component | Purpose | Priority |
|---|---|---|
| DNS provider API | Programmatic DNS record management (OVH, Gandi, Cloudflare) | High |
| DNS-01 ACME | Wildcard certs + domains without port 80 access | High |
| Unified Poke flow | Single action to expose service on all channels | Medium |
| Peek aggregation | Combined view: local scan + mesh peers + DNS records + Tor | Medium |
| Emancipate orchestrator | Atomic multi-channel activation with rollback | Medium |
DNS Provider API Integration
Design
New package: secubox-app-dns-provider
package/secubox/secubox-app-dns-provider/
files/etc/config/dns-provider # UCI: provider type, API keys, zone
files/etc/init.d/dns-provider # (optional) cron for record sync
files/usr/sbin/dnsctl # CLI: record add/rm/list/sync
files/usr/lib/secubox/dns/ # Provider adapters
ovh.sh # OVH API (app key + secret + consumer key)
gandi.sh # Gandi LiveDNS (API key)
cloudflare.sh # Cloudflare (API token + zone ID)
UCI config
config dns_provider 'main'
option enabled '1'
option provider 'ovh' # ovh | gandi | cloudflare
option zone 'example.com' # managed DNS zone
config ovh 'ovh'
option endpoint 'ovh-eu' # ovh-eu | ovh-ca | ovh-us
option app_key ''
option app_secret ''
option consumer_key ''
config gandi 'gandi'
option api_key ''
config cloudflare 'cloudflare'
option api_token ''
option zone_id ''
dnsctl commands
dnsctl list # List all DNS records in zone
dnsctl add A myservice 1.2.3.4 # Create A record
dnsctl add CNAME blog mycdn.net # Create CNAME
dnsctl rm A myservice # Remove record
dnsctl sync # Sync local vhosts to DNS records
dnsctl verify myservice.example.com # Check DNS propagation
acme.sh DNS-01 integration
Once dnsctl works, enable DNS-01 challenges in haproxyctl cert add:
# Current (HTTP-01 only):
acme.sh --issue -d "$domain" --webroot /var/www/acme-challenge
# New (DNS-01 via provider):
provider=$(uci -q get dns-provider.main.provider)
case "$provider" in
ovh)
export OVH_END_POINT=$(uci -q get dns-provider.ovh.endpoint)
export OVH_APPLICATION_KEY=$(uci -q get dns-provider.ovh.app_key)
export OVH_APPLICATION_SECRET=$(uci -q get dns-provider.ovh.app_secret)
export OVH_CONSUMER_KEY=$(uci -q get dns-provider.ovh.consumer_key)
acme.sh --issue -d "$domain" --dns dns_ovh
;;
gandi)
export GANDI_LIVEDNS_KEY=$(uci -q get dns-provider.gandi.api_key)
acme.sh --issue -d "$domain" --dns dns_gandi_livedns
;;
cloudflare)
export CF_Token=$(uci -q get dns-provider.cloudflare.api_token)
export CF_Zone_ID=$(uci -q get dns-provider.cloudflare.zone_id)
acme.sh --issue -d "$domain" --dns dns_cf
;;
esac
This unlocks wildcard certificates (*.example.com) and domains behind firewalls without port 80.
The Emancipate Flow
When a user pokes a service and chooses "Emancipate", the orchestrator runs all selected channels atomically:
User selects: Gitea (port 3001) → Emancipate [Tor + DNS + Mesh]
1. Tor channel:
secubox-exposure tor add gitea 3001 80
→ .onion address generated
2. DNS channel:
dnsctl add A gitea <public-ip>
haproxyctl vhost add gitea.example.com 3001
haproxyctl cert add gitea.example.com --dns
→ HTTPS live at gitea.example.com
3. Mesh channel:
secubox-p2p publish gitea 3001 "Gitea"
gossip_sync
→ All mesh peers discover the service
4. Registry update:
Service registry refreshed
Landing page regenerated
Exposure dashboard shows all three badges
Rollback on failure
If any channel fails, previously completed channels are not torn down — they remain active. The failure is reported, and the user can retry or remove individual channels via the Exposure dashboard toggles.
Peek: What Exists Today
The current Exposure dashboard (luci-app-exposure/services.js) already implements Peek:
- Scans all listening ports via
netstat -tlnp - Enriches with real names from uhttpd, streamlit, Docker, glances configs
- Cross-references Tor hidden services by backend port
- Cross-references HAProxy vhosts by backend port
- Shows toggle switches for Tor and SSL per service
What Peek needs next
- DNS records column: Show which services have DNS A/CNAME records via
dnsctl list - Mesh visibility column: Show which services are published to mesh peers
- Multi-node view: Aggregate services across all mesh peers (already available via
secubox-p2p get_shared_services)
Poke: What Exists Today
The toggle switches in the Exposure dashboard are already "Poke" actions:
- Toggle Tor ON → modal → service name + onion port → Enable
- Toggle SSL ON → modal → service name + domain → Enable
What Poke needs next
- DNS toggle: Third toggle column for DNS record management
- Emancipate button: "Expose everywhere" single action per service
- Provider selection: Choose which DNS zone/provider for the domain
Integration Points with Existing Packages
| Package | Integration | Direction |
|---|---|---|
secubox-app-exposure |
Peek scan + Tor/SSL add/remove | Already working |
secubox-app-haproxy |
HAProxy vhost + ACME cert | Already working |
secubox-app-tor |
Hidden service lifecycle | Already working |
secubox-p2p |
Service publish + gossip sync | Add publish RPC call |
luci-app-exposure |
Dashboard: add DNS column + Emancipate button | Frontend extension |
secubox-app-dns-provider |
NEW: DNS record CRUD via provider APIs | To build |
luci-app-dns-provider |
NEW: LuCI config for provider credentials | To build |
Implementation Order
secubox-app-dns-provider— CLI tool + UCI config + provider adapters (OVH first)- DNS-01 in haproxyctl — Wire
dnsctlinto ACME flow as alternative to HTTP-01 luci-app-dns-provider— LuCI frontend for provider configuration- Exposure dashboard DNS column — Add DNS toggle +
dnsctlintegration - Emancipate flow — Unified orchestrator in
secubox-exposure emancipate - Mesh publish integration — Wire
secubox-p2p publishinto Emancipate
Naming Convention
The project uses punk/DIY metaphors:
| Term | Meaning | Technical equivalent |
|---|---|---|
| Peek | Discover, scan, observe | secubox-exposure scan + service registry |
| Poke | Target, configure, aim | Toggle switches + modal config |
| Emancipate | Activate, make free, expose | Multi-channel atomic activation |
| Station | A SecuBox node | One OpenWrt device running the mesh |
| Generative | Each station can create new endpoints | Docker apps + exposure channels |
Security Considerations
- DNS provider API keys stored in UCI with restricted ACL
- ACME private keys in
/etc/acme/with 600 permissions - Tor hidden service keys in
/var/lib/tor/owned by tor:tor - Emancipate flow never exposes 127.0.0.1-only services (guard in scan)
- DNS records only created for services the user explicitly Pokes
- Rollback does NOT auto-delete — user must explicitly remove exposure