{"name":"membership.nu","source":"// examples/membership.nu — a SWIM membership node over the pubkey overlay\n// (TODO §7.4 Phase 5): net/failuredetector drives net/membership, and every\n// probe/ack/gossip rides net/transport addressed by PUBLIC KEY (direct via\n// securedgram when possible, relayed otherwise). This is the adapter that\n// turns the pure FD step machine into a live detector.\n//\n//   ./nurl.sh examples/membership.nu <relay_host> <relay_port>\n//\n// (Illustrative wiring — run a relay with examples/relay.nu and several of\n// these; each learns the others via net/rendezvous in a full deployment.)\n\n$ `stdlib/core/string.nu`\n$ `stdlib/core/vec.nu`\n$ `stdlib/std/async.nu`\n$ `stdlib/std/time.nu`\n$ `stdlib/ext/env.nu`\n$ `stdlib/net/relay.nu`\n$ `stdlib/net/transport.nu`\n$ `stdlib/net/membership.nu`\n$ `stdlib/net/failuredetector.nu`\n\n@ my_pk → ( Vec u ) { : ( Vec u ) v ( vec_new [u] ) : ~ i k 0 ~ < k 32 { ( vec_push [u] v # u + 1 k ) = k + k 1 } ^ v }\n\n// Send a membership message (PING/ACK/PING-REQ + gossip) to a peer pubkey\n// over the transport — direct or relayed, the FD neither knows nor cares.\n@ send_msg * Transport tr ( Vec u ) dst i mtype i seq * PkMemberTable tbl → v {\n    : ( Vec s ) g ( pktable_gossip tbl 6 )\n    : PkMsg m @ PkMsg { mtype seq ( vec_new [u] ) g }\n    : ( Vec u ) wire ( pkmsg_encode m )\n    ?? ( transport_send tr dst wire ) { T _ → {} F _ → {} }\n    ( pkmsg_free m ) ( vec_free [u] wire )\n}\n\n// Perform one FD action on the wire.\n@ do_action * Transport tr FdAction a * PkMemberTable tbl → v {\n    ? == . a kind ( fd_do_ping ) { ( send_msg tr . a target ( pk_ping ) . a seq tbl ) } {}\n    ? == . a kind ( fd_do_preq ) {\n        // ask each relay to probe the target on our behalf\n        : i n ( vec_len [s] . a relays )\n        : ~ i k 0\n        ~ < k n {\n            : s pp ?? ( vec_get [s] . a relays k ) { T x → x F → # s 0 }\n            ? != # i pp 0 { : *PkMember rm # *PkMember pp ( send_msg tr . rm pubkey ( pk_pingreq ) . a seq tbl ) } {}\n            = k + k 1\n        }\n    } {}\n}\n\n// Drain inbound messages, feeding acks/gossip to the FD and answering pings.\n@ pump * Transport tr * FdState fd * PkMemberTable tbl ( Vec u ) self_pk i now → v {\n    : ~ b more T\n    ~ more {\n        ?? ( transport_recv tr 50 ) {\n            T msg → {\n                : PkMsg m ( pkmsg_decode . msg payload )\n                ( fd_on_gossip fd m now )\n                ? == . m mtype ( pk_ack ) { ( fd_on_ack fd . m seq now ) } {}\n                ? == . m mtype ( pk_ping ) { ( send_msg tr . msg src ( pk_ack ) . m seq tbl ) } {}\n                ( pkmsg_free m )\n                ( transport_msg_free msg )\n            }\n            F → { = more F }\n        }\n    }\n}\n\n@ main → i {\n    : i argc ( env_args_count )\n    : String host ? > argc 1 ( env_arg 1 ) ( string_from `127.0.0.1` )\n    : i port ? > argc 2 {\n        : String ps ( env_arg 2 ) : i p ( nurl_str_to_int ( string_data ps ) ) ( string_free ps ) p\n    } 47700\n\n    ?? ( relay_dial ( string_data host ) port ) {\n        T rc → {\n            : ( Vec u ) self_pk ( my_pk )\n            ?? ( relay_register rc self_pk ) { T _ → {} F _ → {} }\n            ( relay_set_timeout rc 200 )\n            : *Transport tr # *Transport ( transport_open # s 0 rc 1 )\n            : *PkMemberTable tbl ( pktable_new self_pk 2000000000 8000000000 3 8 )\n            // ( transport_add_peer + pktable_apply for each known peer … )\n            : *FdState fd ( fd_new # s tbl 1000000000 300000000 2000000000 3 )\n\n            : ~ i ticks 0\n            ~ < ticks 3 {\n                : i now ( monotonic_ns )\n                : FdAction a ( fd_tick fd now )\n                ( do_action tr a tbl )\n                ( fd_action_free a )\n                ( pump tr fd tbl self_pk now )\n                : ( Vec s ) dead ( fd_sweep fd now )\n                ( __pk_dead_free dead )\n                ( sleep_ms 200 )\n                = ticks + ticks 1\n            }\n            ( nurl_print `membership node ran (members: ` ) ( nurl_print_int ( pktable_count tbl ) ) ( nurl_print `)\\n` )\n\n            ( fd_free fd ) ( pktable_free tbl ) ( transport_free tr )\n            ( vec_free [u] self_pk ) ( relay_close rc )\n        }\n        F e → ( nurl_print `could not reach relay\\n` )\n    }\n    ( string_free host )\n    ^ 0\n}\n","bytes":4290}