Skip to content

feat: OpenPlanter Crowd — local Nostr-compatible task market (REPL/TUI + Tauri) - #35

Open
erkinalp wants to merge 20 commits into
ShinMegamiBoson:mainfrom
erkinalp:devin/crowd-feature
Open

feat: OpenPlanter Crowd — local Nostr-compatible task market (REPL/TUI + Tauri)#35
erkinalp wants to merge 20 commits into
ShinMegamiBoson:mainfrom
erkinalp:devin/crowd-feature

Conversation

@erkinalp

@erkinalp erkinalp commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Introduces OpenPlanter Crowd: a local, nostr-compatible task market with durable signed event storage, cross-process atomic claims, and optional strfry relay/router federation. Agents can publish leaf tasks, claim work from peers, return results, cancel tasks, advertise availability, and maintain a trust list — from the Python REPL/TUI and the Tauri desktop chat.

Key changes

  • agent/crowd.py
    • CrowdStore persists every signed event to events/<task_hash>/<event_id>.json and hydrates MemoryRelay from disk on CrowdClient creation, so new processes see all prior events.
    • CrowdStore uses threading.Lock plus an optional filelock on .openplanter/crowd/.crowd.lock, making claim, cancel, and result writes safe across separate Python processes.
    • CrowdClient.start_local_relay() starts a local strfry relay when --crowd-strfry is set and, when crowd_relays are configured, also spawns a strfry router to relay 3100131005 events up/down those upstreams.
    • CrowdClient no longer publishes orphaned events: it signs, persists to the store atomically, then publishes to the in-memory relay.
  • agent/__main__.py now passes auto_spawn_strfry to CrowdClient and shows the active upstream federation in the startup banner.
  • agent/crowd_cli.py and agent/tui.py continue to reuse the same crowd_client_from_config path, so desktop and REPL share the same durable store and identity.
  • pyproject.toml adds filelock as a dependency.
  • CROWD.md and README.md updated for durable events, locking, and federation.

Design choices

  • Tauri commands still shell out to python3 -m agent.crowd_cli to keep BIP-340 signing and storage in one place and identical between CLI and desktop.
  • strfry is still optional; the out-of-the-box mode is in-memory local-only, but events now survive process restarts because they are written to disk.
  • The filelock dependency is optional at import time: if it is not installed, the store falls back to a thread lock only.

Usage

Python:

openplanter-agent --workspace ./my-project --crowd

or with strfry federation:

openplanter-agent --workspace ./my-project --crowd --crowd-strfry
# after setting crowd_relays in .openplanter/settings.json

Testing

  • python3 -m py_compile agent/crowd.py agent/__main__.py agent/crowd_cli.py agent/tui.py
  • CLI smoke test: publish → list → claim → list → cancel → trust passes
  • Cross-process claim race test: two concurrent crowd_cli claim processes, only one succeeds
  • cargo check -p op-tauri passes
  • npm run build and npm test pass (200/200)

devin-ai-integration Bot and others added 20 commits July 20, 2026 19:52
- agent/crowd.py: identity, Nostr-style event model, task store, in-memory relay, strfry wrapper, DP embeddings, high-level client
- SessionStore now exposes .crowd (CrowdStore)
- add /crowd, /claim, /trust slash commands
- add persistent settings: crowd_relays, crowd_nsec, crowd_worker_tags, crowd_epsilon
- add --crowd*, OPENPLANTER_CROWD* config flags

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- CrowdIdentity now signs Nostr events with coincurve.sign_schnorr.
- 32-byte x-only public keys and 64-byte signatures (hex) for real relay compatibility.
- Fallback to HMAC placeholder only when coincurve is unavailable.
- Add coincurve>=21.0.0 to project dependencies.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- Add CROWD.md with quick start, CLI/env config, event kinds, signing,
  storage layout, relay federation, and programmatic API.
- Surface crowd flags and env vars in README.md.
- Update project structure and dependency list in README.md.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- Add CrowdStore.cancel_task() (sets status to 'canceled' if not finished).
- Add CrowdClient.cancel_task() that publishes a signed kind:31006 event.
- Add /cancel <hash-or-prefix> to the REPL/Textual TUI.
- Update CROWD.md and README help text.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Add the same /crowd, /claim, /cancel, and /trust slash commands from the Python REPL to the Tauri desktop chat.

- New agent/crowd_cli.py headless CLI wrapper around agent/crowd.py
- New Tauri IPC commands in crates/op-tauri/src/commands/crowd.rs that invoke the Python CLI
- Frontend API wrappers and slash dispatch in openplanter-desktop/frontend
- Update CROWD.md and README.md with desktop usage instructions

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- Use compact JSON separators for event id to match NIP-01.\n- Simplify strfry root to store.root so absolute session dirs work.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- PersistentSettings.normalized() now preserves crowd_relays, crowd_nsec,
  crowd_worker_tags and crowd_epsilon.
- --crowd-relay-port only overrides env when explicitly provided.
- A single CrowdClient is created at startup, stored in ChatContext, and
  stopped on exit.
- Generated crowd_nsec is persisted in SettingsStore from REPL, TUI and CLI.
- CrowdIdentity only falls back to HMAC placeholder when coincurve is missing.
- MemoryRelay invokes subscriber callbacks outside the non-reentrant lock.
- CLI claim/cancel resolve tasks against open status only, matching the TUI.
- _crowd_client caches the built client in ChatContext.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…eration

- Persist every signed Nostr event to events/<task_hash>/<event_id>.json.
- Add CrowdStore.append_event/list_events/iter_all_events and hydrate MemoryRelay from disk.
- Use filelock + threading.Lock for atomic cross-process claim/cancel/result writes.
- Start the strfry router when upstream_relays are configured and --crowd-strfry is set.
- Update CROWD.md and README for durable events, locking, and federation.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…mand, noisy embedding docs

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
- Add a real WebSocket Nostr relay adapter in agent/relay.py and a small
  relay_server.py for local integration tests.
- Wire RelayPool into CrowdClient so two processes can exchange tasks,
  claims, results, and cancellations over ws://.
- Complete marketplace lifecycle: publish, discover, claim, result,
  accept/reject/expire/reopen.
- Add private-task support: encrypted briefs (AES-GCM + ECDH per allowed
  private identity), sanitized context bundles, scoped relay access, and
  separate public/private identities persisted in settings.
- Rename the placeholder differential-privacy class to NoisyEmbedding and
  document it as randomized matching.
- Add automated integration tests covering event verification, public
  lifecycle, encrypted task exchange, cancel propagation, and atomic
  simultaneous claims.
- Fix x-only ECDH parity for odd-y private keys so encrypted briefs can be
  decrypted by any BIP-340 identity.

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…tings keys, restrictive perms

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…ngs plaintext fallback, private relay fallback, REPL client config, event ordering, strfry sync window, publish-private allowed default, TUI epsilon

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…ay flush, encrypted settings overwrite guard

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
…de claim limit

Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
… OK response

- Only the task publisher can cancel/accept/reject/expire/reopen (enforced in CrowdClient.cancel_task, CrowdStore lifecycle methods, and _ingest_event for remote events).

- Split task identity into brief_hash (deduplication/provenance) and task_id = H(publisher || nonce || brief_hash) so identical briefs from the same or different publishers no longer collide.

- Publish task events with task_id, brief_hash, publisher, and nonce in content and use task_id as the NIP-33 d-tag/local store key.

- Fix bundled relay OK response to use a boolean third element and a separate reason string per NIP-01.

Validation: py_compile, pytest tests/test_crowd.py (5), cargo check -p op-tauri, npm test (200), npm run build.
Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Co-Authored-By: Erkin Alp Güney <erkinalp9035@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant