v1 plan: peer-to-peer support/ticketing built on hero_collab snapshot #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
v1 plan — hero_assistance
Master tracking issue. SSOT for the v1 plan; supersedes prior chat. v1 is production-grade for real customers, not a demo.
§0 Current state (updated session 37, 2026-05-06)
Engineering-complete full-stop. Phase 23 (agentic-calling adoption) closed in s37 — the final pre-customer engineering item per s35's framing. v0.3.1 tagged + auto-published via the s32 release-on-tag CI workflow. Twenty-one locked decisions (D-01..D-13 + D-15..D-21). Open limitations: L-01 (manual config), L-02 (no web gateway), L-05 (macOS no PDEATHSIG), L-07 (cross-host CAP_NET_ADMIN — narrowed s34: wire-shape gap closed, runtime-environment gap stays open). Closed: L-03 + L-04 (s33), L-06 (s34). Test posture: 225 native passing (was 222 → +3 phase23 tests) / 32
_ui_wasmlib / 13 ignored / 0 failed (modulo documentedphase10_multi_project_merged_stream_tags_by_project_idStreamMap-yield-order flake — passes in isolation; pre-existing since s12).Sessions 24–37 — what shipped since the last §0 refresh
wry::register_uri_scheme_protocol(assist://scheme + vendored 232 KiBbootstrap.min.css+ 484 KiBbootstrap-icons.min.css); typeddioxus-bootstrap-cssmigration across 7 files; chat composer pattern; paste-reads-real-bytes viaBlob.array_buffer(); drag-and-drop image upload; inline image-thumbnail attachments; Linux-screenshot-paste fix. D-18 filed (spec-as-checklist).docs/vision/HERO_ASSISTANCE.md+docs/dev/e2e_checklist.md(90 rows, 41 wired-green at session-end). 4-port browser-deployable demo scheme (8083-8086).docs/dev/architecture.md+docs/dev/testing.md(7-layer pyramid as first-class artefact)._appdesktop → 4 desktop bug fixes + UI guard for the 5th. 7 desktop findings recorded.ProjectTabrefactor + auto-pickselected_tabrender-loop fix). v0.2.0 tagged. Bonus same-session: CI release-on-tag workflow (issue #6 closed); v0.2.1 tagged + Generic package published.users.display_namecolumn + partial UNIQUE INDEX onemail != ''+ pre-flight duplicate detection + 3 inherited L-03 tests formally#[ignore]d. v0.2.2 tagged./eventsGET on TCP listener with per-recipient server-side filter (D-20 privacy invariant) + SDK transport polymorphism (RpcEndpoint/EventsTransportenums + generifiedenvelope_frames). v0.3.1 tagged.prompt.mdtrimmed from 322 lines / 228 KiB to 251 lines / 32 KiB by enforcing its own §1 one-line + §2 pointer rules; verify block byte-preserved. No source change.summary+ structurederrors[]+x-hero-intentmetadata (schema declared atinfo.x-hero-intent-schema; consumers tolerate unknown values per open-enum semantics). 3phase23_*regression tests added (rpc.discoverwas pre-existing — Phase 4 reduced to "confirm what's wired"). New docs:docs/dev/agentic_calling.md(identity-pinning contract) +docs/dev/agentic_workflows.md(5 example LLM-generated Python scripts; doubles as router-integration fixtures). Vision-doc MCP-language reframed in §1 + §2.5 + §3 + §4.7 + §5.4. D-21 filed locking the agentic-calling contract: hero_router pattern over MCP; FNV-1a-64 (NOT MD5) over UDS rpc.discover; rpc.discover unauthenticated by Hero convention; sandbox MUST setX-Hero-Userfrom human-user identity hero_router authenticated. D-04 + D-20 invariants preserved unchanged —phase22_events_tcp_per_user_filter_excludes_non_recipientdoubles as agentic-calling privacy regression gate. B.5 caught 3 load-bearing problems before code (FNV vs MD5 / UDS vs HTTP / x-hero-intent axiom-growth). Issue #8 closes.Open issues / next work
Discretionary forward work (no priority order; pick when relevant)
_ui/openrpc.jsonHTTP proxy (D-21 DX nicety — router uses UDS, so this is dev-ergonomic only). 0.5 session.x-hero-intent.identity_requirementper-method when customer policy surfaces (today's audit ships everything asenrolled-user; future deployments may wantsupport-roleforchannel.delete/attachment.cleanup_pending/group.rights.set/ etc.).EventsTransport::Http(reqwest doesn't expose source-address binding).Closed since s23
Summary
A peer-to-peer support/ticketing system for the Hero stack. Two OpenRPC services in one monorepo, built by snapshot-copying hero_collab and pruning. Many-to-many topology — a client subscribes to multiple project servers; a server hosts multiple users (customers + support agents).
hero_assistance_server— one per customer-project. SQLite, OpenRPC, mycelium-bound TCP listener. Owns tickets, threaded messages, image attachments, and presence for that project.hero_assistance_ui_wasm) styled withdioxus-bootstrap-css(Bootstrap 5.3.3). Embeddable as a "Support" tab in any Hero (Dioxus) host app via native component embed; standalone Dioxus desktop binary (hero_assistance_app) hosts the same component graph full-screen for users juggling multiple projects. The inheritedhero_assistance_uiAxum/Askama crate (from the collab snapshot) bridges the migration as a dual-mode server (--dist <path>for WASM dist + askama fallback) and owns the/rpcJSON-RPC proxy that the SPA consumes viagloo-net.Specs (chat 2026-04-27)
Architecture
Three-layer identity
A user has one account keyed on email. Multiple devices per user; each generates its own mycelium identity. Server-side
device_bindingstable maps mycelium_addr → user_id. Sales pre-enrolls customers by email; first connection from any device receives a magic link to bind that device.Transport
Mycelium-IPv6. Server binds an Axum listener —
auto:<port>mode broad-binds[::]:portwithIPV6_V6ONLY=1and the dispatcher enforcespeer_addr ∈ 400::/7(D-08; supersedes D-05's bind-to-TUN clause after the verification sweep showed mycelium 0.7.4'sgetInfo.nodeSubnetreturns a /64 network base, not a host address). On accept,peer_addr.ip()∈400::/7is the cryptographically-authenticated peer device address — kernel source-address validation prevents non-mycelium local processes from forging overlay sources, so the predicate is a sufficient cryptographic-attestation gate. Standalone client bundles a mycelium daemon as a subprocess (detects + reuses an existing system mycelium UDS or upstream-TCP API if present; otherwise spawns its own with a per-app data directory +prctl(PR_SET_PDEATHSIG, SIGTERM)on Linux for kernel-level cleanup; identity key generated on first run viaOpenOptions::create_newfor atomic race-safe creation — Phase 7).Storage
SQLite, inherited from hero_collab. New tables:
users { id, email PRIMARY, display_name, role, status, created_at }device_bindings { mycelium_addr PRIMARY, user_id FK, device_label, bound_at, last_seen }Existing collab tables retained: messages, threads, attachments, presence — semantically remapped to tickets/comments/etc.
UI (D-09)
Dioxus 0.7 single-page WebAssembly application styled with
dioxus-bootstrap-css(Bootstrap 5.3.3, pinned to gitdevelopmentbranch). Embedded as a "Support" tab inside other Hero (Dioxus) host apps via native component embed (typed props, shared signals, shared theme/auth, same render tree as host); standalone via thehero_assistance_appDioxus desktop binary for support agents juggling multiple customer-projects. The browser SPA talks to_ui's Axum server at/rpcviagloo-netJSON-RPC;_uiproxies to_serverover UDS exactly as the inherited collab pattern does. The argument for Dioxus over Axum/Askama (which the collab snapshot inherited) is product-fit: hero_assistance is realtime-conversational, embed-as-Support-tab is the primary deployment, the wire schema will evolve over years, and the standalone binary is natural in Dioxus / awkward in Askama. Migration recipe inherits from thedioxus_bootstrapHero skill (17-repo precedent provides tooling and confidence; not the argument for adoption). Visual regression policy:<1%perceptual diff via canary-wait + 3-run + pixelmatch against captured Askama baselines.Crate layout
Per D-09: D-03's
_clientslot becomes_ui_wasm;_coreslot is not materialized (typed shared types live in_sdkand the OpenRPC spec; no other consumer warrants a separate crate);_appslot is restored as a Dioxus desktop binary; new_uislot kept from the collab snapshot as the migration bridge.Locked decisions
_client→_ui_wasm, no_core,_apprestored as Dioxus desktop, new_uiAxum bridge slot from collab snapshot).CallerIdentity{user_id, device_addr, role}newtype from day 1. Identity boundary at hero_collabmain.rs:210–249; ~15 call sites updated.getInfo.nodeSubnetreturns a /64 network base, not a host address; daemon-bundling and threat model stand.[ipv6]:portTCP); rebind-on-conflict fordevice_bindings;peer_addrcanonicalized viaIpv6Addr::to_string();peer_addrthreaded separately fromcaller;mycelium_sdkworkspace dep deferred.auto:<port>broad-binds[::]:portwithIPV6_V6ONLY=1; dispatcher enforcespeer_addr ∈ 400::/7. Supersedes D-05's bind-to-TUN clause.is_mycelium_overlay()predicate;enforce_overlay_peerAppState flag. Phase 6 design notes for spawn-own (PDEATHSIG,O_CREAT|O_EXCLkey-gen) recorded for inheritance — now Phase 7's design constraints.dioxus-bootstrap-css; dual-mode Axum_uiretains existing crate as fallback +/rpcproxy; amends D-03 crate slots. Migration recipe is thedioxus_bootstrapHero skill (canonical pattern, 17-repo precedent).ticket.*andcomment.*are added as new dispatcher arms + spec method blocks pointing at the samehandlers::channel::*/handlers::message::*functions; tables, handler module symbols, and CSS class names stay named per the inherited collab snapshot until the post-v1 cleanup that dropsaskama_templates. The inheritedchat-app.jskeeps callingchannel.*/message.*._sdk(per-device merge), not_server(server-side fan-in proxy) — preserves D-04's per-project identity boundary, no new server-side trust boundary.MultiProjectClientwraps N rpc.sock + N events.sock UDS readers and merges viatokio_stream::StreamMapkeyed by caller-supplied opaqueProjectId.viewing_ticket_idschema: new wire RPCpresence.set_viewing_ticket { ticket_id: u64 \| null }+ newUserEvent::PresenceViewingChannel { user_id, channel_id: Option<u64> }fanout variant + newviewing_channel_idJSON field onusers.data— additive only. Server-side existence check on non-nullticket_id(SELECT 1 FROM channels) returning NotFound (-32003) is the load-bearing referential-integrity gate._appshell =dioxus desktop: D-09 §"Deferred questions" bias applied (Tauri rejected for the JS-bridge contradiction with D-09 §"Argument" pt 5). Phase 11 deliberately does NOT consume_ui_wasmas a path-dep (itsgloo-net-driven RPC is browser-only); cross-target consumption + desktop RPC plumbing land at Phase 13/14.tokio::net::TcpSocket+device_bindings.mycelium_addrSQLite read on server B = canonical<overlay_a>, NOT loopback). Events-over-TCP transport deferred to Phase 12c per L-06. Siblingspawn_own_with_options(not breaking). Test#[ignore]-by-default per L-07.Limitations accepted
userstable missingUNIQUE(email)+display_name(D-04 schema gaps deferred from Phase 3); may bundle with Phase 9bchat.htmlmigration if display names render, otherwise post-v1.119eabd): macOS PDEATHSIG gap —mycelium_runtime::spawn_ownreturnsErr(SpawnOwnError::UnsupportedPlatform)on macOS/Windows sincePR_SET_PDEATHSIGis Linux-only.detect()still works on those platforms, so users running a system mycelium daemon are unaffected; only fresh-install macOS without a system mycelium hits the gap. Loud-error chosen over silent-orphan-on-force-kill. kqueue-based parent-watcher noted as post-v1 future work.70f0cf5): Events fanout transport is UDS-only —_server's TCP listener serves only/rpc(no/eventsSSE/WebSocket counterpart);MultiProjectClient::registeris UDS-only on both rpc and events sockets. Cross-hostMultiProjectClientblocked at multiple layers; single-host story unaffected. Phase 12c (post-v1) ships server-side/eventsSSE + SDK TCP-events-stream.70f0cf5): Phase 12b cross-host smoke test is#[ignore]-by-default — spawning a real mycelium daemon needsCAP_NET_ADMINfor TUN creation, which CI does not grant. Hermetic SDK unit tests DO run in default CI. Live cross-host smoke is a release-runbook procedure:sudo setcap cap_net_admin+ep $(which mycelium); cargo test -- --ignored phase12b.Explicitly deferred — NOT in v1
None of these were in the original spec; all are real, all post-v1:
hero-assist://...)_uipost-migration (default-on through v1 ship; cleanup tracked separately)dioxus desktopruns short on platform featuresPhase plan (renumbered 6→14 per D-09 §"Phase plan after D-09")
/bootstrap); D-01..D-05 + L-01..L-02 written; initial repo skeleton committedd56bb5dbc06df1e3fee9aCallerIdentitynewtype,users+device_bindingsschema, magic-link enrollment RPCc7acf2epeer_addrextraction,device_bindingsupserta9df3b7auto:<port>overlay-bind + dispatcher-level400::/7filter (supersedes D-05 bind-to-TUN clause)5c4e8b65eec381mycelium_runtimehelper in_sdk(detect-or-spawn with PDEATHSIG + atomic key-gen) + L-05 macOS gap119eabd_ui_wasmcrate skeleton + dual-mode_uiserver (--distflag + askama-as-optional-feature)b6d9518index.html(763 lines) → Dioxus components; capture Askama visual-regression baselinesd492f58chat.htmlbody → DioxusTicketsPage; channels→tickets / message→comment via additive RPC aliasing (D-10)0756f4b+1674b05editor.html(429 lines) → DioxusEditorPage+ editor canary drift fix8b50bdcMultiProjectClientin_sdk) +presence.set_viewing_ticketRPC +PresenceViewingChannelfanout variant (D-11 + D-12)c8dc6b4+9c3c4e7hero_assistance_appstandalone Dioxus desktop binary (D-13; consumesMultiProjectClient; env-var subscriptions)370c0ddwait_for_two_mergedhelper)2f29be6MyceliumOptions+ cross-daemon-peer attestation; events-over-TCP deferred (L-06);#[ignore]-by-default (L-07)70f0cf5+8fda9c55515bf25ca7cfbd474b84(source) +62962cd(docs); tagv0.1.0_sdk—RpcTransporttrait + UDS/TCP/gloo-net impls; refactorHeroAssistanceClientto be transport-generic; foundation for embed-ready architecture_appconsumes_ui_wasm(path-dep,desktopfeature) + Rule-6 walkthrough gateDone: phases 0-14 (v0.1.0 tagged). Remaining: Phase 15a-d + 15-release (v0.2.0 — customer-facing UI). Phase 15 is ~6-9 sessions; closes acceptance criteria 2-8 from the list below. Post-v1 backlog after v0.2.0: web gateway (L-02), events-over-TCP transport (L-06; closure path simplified by Phase 15a foundation), full D-10 storage rename, mobile, AI-anything (all explicitly deferred).
v1 acceptance criteria
Demonstrable on real cross-host machines.
_appwithout separately installing myceliummycelium_runtime::spawn_own, Linux only per L-05)_apppresence.set_viewing_ticket); no UIMultiProjectClientworks; env-var only_ui_wasm::Appinto_appright pane<1%on migrated pages_appright pane in 15d)kickstart.shexits 0v0.1.0 closes 4 of 11. Phase 15 closes the remaining 7.
Risks / known unknowns
dioxus-bootstrap-csspinned to gitdevelopmentbranch absorbs API drift; we accept the rebase tax. Bounded; not architectural.chat.htmlmigration scope — 1,821 lines is the single largest migration risk; Phase 9b allocated dedicated, with split-into-9b.1+9b.2 as a fallback if the diff blows up.prctl; macOS_appmay orphan a mycelium child on force-kill. kqueue parent-watcher is out of scope for v1.Methodology
This project uses the ai-pipeline v1 multi-session workflow. Local workspace at
/home/pctwo/Documents/temp/assistance_work/. Each phase above corresponds to one or more sessions;prompt.md §3carries the next-session entry point. UI migration phases (8–9c) inherit from thedioxus_bootstrapHero skill (canonical recipe; 17-repo precedent for tooling and confidence). Visual regression useshero_browserMCP for screenshots +pixelmatchfor diff.Branching & commits
Hero conventions: default branch
development; feature branchesdevelopment_<name>(underscores); squash-merge back todevelopment. Conventional-commit messages with absolute issue URL reference; no AI attribution.Phase 13b ✅ — UX validation walkthrough findings (session 17, 2026-04-30)
Commit:
5ca7cfbAgent-driven curl walkthrough against the Phase 13a demo (acme + globex; both
events.socktaps). Eight findings recorded inruns/phase13_findings.md:ticket.update/ticket.createrejects human-readable names because the inherited slug validator (^[a-z0-9][a-z0-9_-]*$) is enforced onname. Seeded tickets bypass via direct SQL — real users can't actually file tickets with normal English titles. Critical UX bug for a ticketing product.ticket.*/comment.*/presence.set_viewing_ticket(three conventions); error messages leak handler-internal column names;comment.createreturns-32603 Internal errorinstead of-32003 NotFoundon bad channel_id;presence.updatebroadcast doesn't carrystatus_text.ticket.archive/update/delete/member.*; dev-mode permission bypass nuance; asymmetricusers.idreferential integrity.11 happy-path checkpoints also recorded (round-trip + fanout for the seven main RPCs; bidirectional cross-project isolation; archive filter; FTS search; clean error UX on the documented error codes) — Phase 14 must not regress these while chasing fixes.
Verdict: product is functional end-to-end; Build phases (0-12b) shipped a working core; none of these block a v1 ship to a friendly first customer; all matter before opening doors wider.
Test posture unchanged (177/3/8 — zero source touched). Demo orchestrator + seed binary worked first-try.
Phase 14 🚧 Next — polish + README + runbook + release
Punch-list (priority order):
comment.createchannel-not-found to-32003 NotFound+ sweep similar handlersstatus_text: Option<String>toUserEvent::PresenceUpdateticket.{archive,update,delete,member.*}--auth-mode=proxyalways"users.idexistence check tocomment.createThen README + runbook + v1 release tag. ~92-95% complete by phase count and effort; one Phase 14 session away from v1 release.
Closing — engineering-complete full-stop per §0 (last refreshed s37). All v1 acceptance criteria have shipped or reached an accepted closure path. Twenty-one locked decisions (D-01..D-13 + D-15..D-21); open limitations L-01/L-02/L-05/L-07 are all post-v1 by original scope. Six tagged releases on `development` (v0.1.0 → v0.3.1) auto-published via the s32 release-on-tag CI workflow.
Remaining work tracked in #7 (customer-readiness gate: v0.2.0 + v0.3.1 Rule-6 walkthroughs + L-07 live cross-host smoke). Discretionary post-v1 items live in `prompt.md §3`; issues filed on pickup.
M3 / v1.0 customer-driven from here.