Say that provider mode is retired, not implemented

f688901e7288 · AtlantisPleb · · parent d773b309f5cd

Say that provider mode is retired, not implemented

Two tracked Pylon docs still opened with "Status: implemented" and told the
reader to run `pylon provider once`, `pylon provider approve-labor`,
`pnpm run provider:serve`, and `pnpm run smoke:nip90-provider`. Every one of
those was deleted on 2026-07-14 by 21e82ce829, along with provider-nip90.ts,
labor-market.ts, multi-earning-ledger.ts, and the wallet stack. A reader
following either page spent the afternoon looking for a command that has not
existed for six weeks.

Both pages now lead with what happened, list the removed commands against what
replaced them, and describe what `pylon provider` actually does today: go-online
and go-offline, local bookkeeping that opens no socket and earns nothing. The
survivors are named where they live — packages/nip90's LBR lane, the recovered
buyer-side request/quote in packages/sarah, the uncalled admission rules in
labor.ts, and the labor-job-state.ts table whose `settle: { delivered: "settled" }`
edge pays a provider's own claim and so must not be reused as-is.

The README carried the same claims plus a `pylon wallet` family that is also
gone; its section is rewritten to match, and the runtime list that trailed it
gets its own heading.

Two code comments were lying in the same direction. nip90-lane-refs.ts said
apps/pylon's provider-nip90.ts re-exports its symbols, and presence.ts said
go-online adds PYLON_NIP90_PROVIDER_CAPABILITY_REF so a provider loop announces
on those relays. The first file is deleted and nothing adds that ref, so
providerDiscoveryFields returns {} in production. Both now say so.

No behavior changes. Reviving the loop still needs the owner-approved design and
invariant change INVARIANTS.md requires, which is why #30 stays open.

Refs #30

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified apps/pylon/README.md
  • modified apps/pylon/docs/labor-market-provider-loop.md
  • modified apps/pylon/docs/nip90-provider-loop.md
  • modified packages/pylon-core/src/presence/nip90-lane-refs.ts
  • modified packages/pylon-core/src/presence/presence.ts

Diff

5 files changed, +246 -243

apps/pylon/README.md modified +27 -69

@@ -634,82 +634,40 @@ node --import tsx apps/pylon/scripts/multi-session-run.ts \

634 634
  --concurrency 2
635 635
```
636 636
637
## NIP-90 Provider Loop
637
## Provider lane (retired market loop)
638 638
639
GO ONLINE for the NIP-90 provider lane is persisted through the provider
640
command:
639
The NIP-90 and NIP-LBR provider loop was deleted on 2026-07-14 by commit
640
`21e82ce829` (`feat(vp1): retire money sites and wallet authority`). `pylon
641
provider once`, `pylon provider approve-labor`, `pnpm run provider:serve`,
642
`pnpm run smoke:nip90-provider`, and the `pylon wallet` family no longer exist.
643
The provider command offers exactly two subcommands:
641 644
642 645
```sh
643 646
pylon provider go-online
644
pylon provider approve-labor --approved-by-ref operator.public.<ref> --job-type code_task
645
pylon provider once
646 647
pylon provider go-offline
647
pnpm run smoke:nip90-provider
648 648
```
649 649
650
`go-online` marks the local runtime online, adds
651
`capability.public.pylon.nip90.text_inference.v0.3` and
652
`capability.public.pylon.labor.local_agent.v0.3`, and records the relay and
653
admission policy that the OpenTUI background loop will use. Labor jobs require
654
an explicit first-run operator approval record from `provider approve-labor`
655
before they execute on a machine. `provider once` is the headless smoke path
656
for one relay loop iteration. The default dashboard starts the same loop
657
automatically only when the persisted lifecycle is `online` or
658
`assignment-ready`. Own-capacity dispatch includes local active
659
`assignment run-no-spend` runners in `load.coding.<service>.busy`, so a
660
machine with two active Codex assignments reports those slots as busy instead
661
of advertising them as still available.
662
663
The provider loop subscribes to the scoped OpenAgents market relay by default,
664
publishes NIP-89 handler info, admits public kind `5050` text-inference
665
requests and OpenAgents labor kinds `5934` code task, `5935` review, and
666
`5936` document work, then publishes NIP-90 `7000` feedback plus result kinds
667
`6050` or `6934`-`6936`. Text inference executes the local Apple FM runtime.
668
Labor jobs execute through the contributor's configured local agent path
669
(`codex`, `opencode`, or `claude`) inside a bounded workspace and return
670
public-safe artifact refs. It uses the shared `@openagentsinc/nip90` package,
671
which re-exports the local `nostr-effect` protocol helpers.
672
673
Environment controls:
674
675
- `PYLON_NIP90_RELAYS`: comma-separated relay URLs. Defaults to
676
  the configured public Nostr relay (default `wss://nos.lol`).
677
- `PYLON_NIP90_PRICE_MSATS`: price floor and requested invoice amount.
678
  defaults to `1000`.
679
- `PYLON_NIP90_REQUEST_TTL_SECONDS`: request age limit. Defaults to one year.
680
- `PYLON_NIP90_MAX_INFLIGHT`: total local inflight admission leases. Defaults
681
  to `1`.
682
- `PYLON_NIP90_PER_BUYER_MAX_INFLIGHT`: per-buyer inflight leases. Defaults
683
  to `1`.
684
- `PYLON_LABOR_AGENT`: optional local labor agent selector: `codex`,
685
  `opencode`, or `claude_code`. If unset, Pylon detects `codex`, then
686
  `opencode`, then `claude`.
687
- `PYLON_LABOR_AGENT_COMMAND`: optional explicit local command prefix for
688
  advanced operators. Pylon appends the generated public-safe labor prompt.
689
690
Wallet boundary: the loop may put a raw BOLT 11 invoice into Nostr relay
691
events because NIP-90 payment-required/result tags require it, but local state,
692
ledger records, OpenAgents API payloads, logs, and issue evidence must only
693
carry public-safe receipt refs, amounts, event ids, and readiness refs. See
694
`docs/nip90-provider-loop.md`.
695
696
Legacy Spark/Breez migration boundary: `pylon wallet migrate-spark` is a
697
preflight-first compatibility path for old v0.2.x balances. It reports missing
698
Breez/Spark credential material as an actionable blocker and only proceeds with
699
explicit local consent. `pylon wallet send --rail spark --confirm-send` is the
700
direct Spark spend/withdraw path for credited Spark wallet funds. It is separate
701
from accepted-work payout authority and emits public-safe refs only. Users must
702
never paste a 12-word mnemonic, raw invoice, Lightning Address, API key, or Spark
703
storage path into GitHub, support threads, logs, or issue comments. See
704
`docs/legacy-spark-wallet-migration.md`.
705
706
Labor boundary: Pylon rejects labor requests that carry provider-auth-shaped
707
material, requests outside the bounded workspace, or a policy ref other than
708
`provider.compliant_usage_labor.v1`. The contributor's own local provider
709
accounts or API budgets stay on the contributor machine. OpenAgents pays for
710
accepted work output only and never resells, proxies, brokers, or transfers
711
provider credentials, sessions, account access, or consumer subscription
712
capacity.
650
`go-online` is local bookkeeping: it marks the runtime lifecycle online, probes
651
Claude, Codex, and Apple Foundation Models readiness, records the resulting
652
capability and blocker refs, and reports per-account coding capacity as
653
quantities. Own-capacity dispatch counts local active `assignment
654
run-no-spend` runners as busy, so a machine with two live Codex assignments
655
reports those slots as busy rather than available. The command opens no socket,
656
publishes no Nostr event, quotes no job, and earns nothing. `go-offline`
657
reverses the lifecycle.
658
659
The protocol package `packages/nip90` survived and grew, and the earning half
660
was revived offline as `openagents provider settle`, which decides what a
661
verified job earned against an NIP-LBR closeout receipt and pays nothing
662
(`payout_rail: "not_connected"`, `custody: "none"`). Reviving the rest needs an
663
owner-approved design and invariant change; `scripts/vp1-retired-money-surface-guard.mjs`
664
enforces that meanwhile.
665
666
For the full record of what was removed, what survived, and what a revival
667
needs, see `docs/nip90-provider-loop.md`, `docs/labor-market-provider-loop.md`,
668
and issue [#30](https://openagents.com/OpenAgentsInc/openagents/issues/30).
669
670
## Runtime
713 671
714 672
The runtime includes:
715 673
apps/pylon/docs/labor-market-provider-loop.md modified +102 -75

@@ -1,75 +1,102 @@

1
# Labor Market Provider Loop
2
3
Issue #4730 (epic #4726). Promise `labor.nostr_negotiation_market.v1`
4
(registry `2026-06-10.25`). Protocol: `docs/nips/LBR.md` (workspace
5
root). Roadmap: `docs/labor/2026-06-10-open-agent-labor-market-roadmap.md`.
6
7
The Pylon's NIP-LBR negotiation lane: watch agentic-coding jobs
8
(kind 5934) on the owned relay, quote the ones this device is
9
capability-true for, execute on acceptance through the labor runtime on
10
the contributor's **own** agent, deliver output-only results
11
(kind 6934).
12
13
## Negotiation discipline
14
15
LBR requests are **never auto-executed**. The lane routes ahead of the
16
generic provider flow in `runProviderJobOnce`:
17
18
1. **Quote.** A kind-5934 request is evaluated by
19
   `evaluateLbrRequestForQuote`: opt-in check, job-kind allowlist,
20
   quote-once, capability truth (`requiredCapabilityRefs` must all be
21
   declared by this Pylon's runtime state), concurrency bound, and the
22
   contributor's price must fit the request's bid. A passing request
23
   gets one kind-7000 quote with the amount and refs. Everything else
24
   gets a typed refusal and no relay traffic.
25
2. **Win.** A kind-7000 LBR acceptance addressed to this provider
26
   (carrying the requester's escrow receipt ref) triggers execution of
27
   the previously quoted job — acceptance without a quote is refused.
28
   acceptance addressed elsewhere is ignored.
29
3. **Execute.** Admission still applies: labor first-run operator
30
   approval (`pylon provider approve-labor`) is required before any
31
   machine runs its first market job. The workspace is bounded under
32
   the Pylon cache. The `claude_code` lane runs through the Claude
33
   Agent SDK runtime (`makeClaudeAgentLaborRuntime`): bounded sandboxed
34
   session, workspace-escape denial, settings isolation, turn and
35
   wall-clock budgets — not a CLI shell-out. Other agent kinds use the
36
   configured labor runtime.
37
4. **Verify, then deliver.** The request's `verificationCommandRef`
38
   maps through the bounded command registry
39
   (`LABOR_MARKET_VERIFICATION_COMMANDS`). Unknown refs are refused.
40
   The command must pass in the workspace or **no result is published**
41
   (`refusal.labor_market.verification_failed`) — the provider never
42
   ships failing work. A passing job publishes the kind-6934 result
43
   with output-only refs and a platform closeout ref.
44
45
## Configuration (contributor-owned, opt-in)
46
47
`laborMarket` section in the Pylon config file, or env overrides:
48
49
```json
50
{
51
  "laborMarket": {
52
    "autoQuote": true,
53
    "priceMsats": 1500000,
54
    "maxConcurrentJobs": 1,
55
    "agentKind": "claude_code"
56
  }
57
}
58
```
59
60
- `autoQuote` defaults to **false** — quoting is opt-in
61
  (`PYLON_LABOR_MARKET_AUTO_QUOTE=1` to enable by env).
62
- `priceMsats` is the contributor's price (`PYLON_LABOR_MARKET_PRICE_MSATS`).
63
  the platform sets no prices.
64
- The local state lives in `labor-market-state.json` (quote records,
65
  public-safe asserted on every write).
66
67
## Boundaries
68
69
The provider never self-accepts and never sees requester funds — it
70
sees an escrow receipt ref. Work runs on the contributor's own agent,
71
own credentials, own machine. Zero provider-auth material in events,
72
artifacts, or state (structurally scanned). The relay is transport, not
73
authority. Settlement truth comes from the platform's escrow/receipt
74
systems (#4729, #4732). Raw session material, diffs, and logs stay
75
on-device. Only refs travel.
1
# Labor market provider loop
2
3
Status: **retired**. The Pylon-side NIP-LBR negotiation lane this page used to
4
describe was deleted on 2026-07-14 by commit `21e82ce829` (`feat(vp1): retire
5
money sites and wallet authority`), along with `apps/pylon/src/labor-market.ts`.
6
No Pylon quotes, wins, or delivers market jobs today.
7
8
The protocol it spoke survives, and so does most of the discipline. This page
9
records what is still real, so the next reader of
10
[#30](https://openagents.com/OpenAgentsInc/openagents/issues/30) starts from the
11
tree rather than from a description of it.
12
13
Protocol: `docs/nips/LBR.md`. Related: `nip90-provider-loop.md`.
14
15
## Commands that no longer exist
16
17
| Removed command                | Status                                                                             |
18
| ------------------------------ | ---------------------------------------------------------------------------------- |
19
| `pylon provider approve-labor` | Deleted. `apps/pylon/src/cli-catalog.ts` offers only `go-online` and `go-offline`. |
20
| `pylon provider once`          | Deleted with the provider loop.                                                    |
21
| `pnpm run provider:serve`      | Deleted with `apps/pylon/scripts/nip90-provider-serve.ts`.                         |
22
23
The `laborMarket` configuration block, the `PYLON_LABOR_MARKET_AUTO_QUOTE` and
24
`PYLON_LABOR_MARKET_PRICE_MSATS` environment overrides, and the
25
`labor-market-state.json` quote store went with the module that read them.
26
`evaluateLbrRequestForQuote` and the `LABOR_MARKET_VERIFICATION_COMMANDS`
27
registry no longer exist anywhere in the tree.
28
29
## What survived
30
31
**The protocol, in `packages/nip90`.** Kept and extended through the
32
retirement:
33
34
- `lbr.ts` — request, quote, acceptance, and result.
35
- `lbr-closeout.ts` — a content-addressed, public-safe receipt binding one whole
36
  lifecycle, carrying `verificationCommandRef`, `testRef`,
37
  `platformCloseoutRef`, and a re-derivable digest.
38
- `lbr-bond.ts` — forfeitable provider bonds, added after the retirement in
39
  `3590489b6e`.
40
41
**The buyer half of negotiation.** `packages/sarah/src/lbr-request-quote/` was
42
recovered on 2026-07-24 as request and quote only. It does not accept, execute,
43
or settle.
44
45
**The admission rules, uncalled.** `apps/pylon/src/labor.ts` still holds the
46
parts of the lane that were about safety rather than money: the first-run
47
operator approval store, the bounded workspace resolver that rejects `..` and
48
`.git` escapes, `evaluateLaborRequestSafety` and its provider-auth
49
exfiltration detector, the public-safe result projection, and the sandboxed
50
local agent command builders for `codex`, `opencode`, and `claude_code`.
51
Nothing imports the module; `makeConfiguredLaborRuntime` sits in
52
`scripts/uncalled-production-symbol-baseline.json`.
53
54
**The wrong half of the state machine.**
55
`apps/pylon/src/coordinator/labor-job-state.ts` also survives, also uncalled.
56
Read it before reusing it. Its transition table contains
57
`settle: { delivered: "settled" }`, and `delivered` is a state the provider
58
sets, so that machine pays a provider's own claim. The doctrine the Pylon arc
59
earned is the opposite: a lease is not an earning claim, and only a settlement
60
receipt is. That rule is not expressible in this table.
61
62
## What replaced the settlement end
63
64
`openagents provider settle` (commit `f0c36de6a0`) settles against the
65
surviving LBR closeout receipt rather than against a provider's submission. An
66
earning requires a receipt that names this job and this provider, was not
67
issued by the provider to itself, carries a verification command _and_ the
68
evidence it produced, carries the platform's own closeout, is
69
content-addressable, landed inside the lease window, and prices the job exactly
70
as the lease did. Ten named refusals cover the failures, and each earns zero.
71
72
Nothing moves: a settled decision reports `payout_rail: "not_connected"` and
73
`custody: "none"`.
74
75
## The boundaries, which still hold
76
77
These were never the part that broke, and they constrain any revival:
78
79
- Pay for verified work only, never for presence.
80
- The provider never self-accepts and never sees requester funds — it sees a
81
  receipt ref.
82
- Work runs on the contributor's own agent, own credentials, own machine. No
83
  provider-auth material enters events, artifacts, or state.
84
- The relay is transport, not authority. Settlement truth comes from the
85
  platform's receipt systems.
86
- Raw session material, diffs, and logs stay on-device. Only refs travel.
87
88
## What a revival still needs
89
90
`INVARIANTS.md` holds that payments, markets, wallet custody, payout, billing
91
credits, and settlement are outside the accepted MVP, and that any revival
92
needs a fresh owner-approved design, custody model, invariant change, and proof
93
program. `scripts/vp1-retired-money-surface-guard.mjs` enforces that on every
94
push.
95
96
Beyond the owner decision, #30 still lists: presence with capacity as a
97
quantity, the claim and lease transport with its idle keepalive and resubscribe
98
(the 2026-06-12 run went dark about a minute after going online because 60
99
seconds of relay silence was treated as fatal), a provider identity seam, and a
100
buyer. Per the issue, the first buyer is OpenAgents' own operation — validator
101
replay jobs, delegation children, and registry service jobs. None of that demand
102
exists as jobs yet.
apps/pylon/docs/nip90-provider-loop.md modified +92 -82

@@ -1,93 +1,103 @@

1
# NIP-90 Provider Loop
1
# NIP-90 provider loop
2 2
3
Status: implemented for `0.3.0-rc1` as the local GO ONLINE provider lane.
3
Status: **retired**. The loop this page used to describe was deleted on
4
2026-07-14 by commit `21e82ce829` (`feat(vp1): retire money sites and wallet
5
authority`). Nothing in the tree serves NIP-90 jobs today.
4 6
5
The provider lane restores the March NIP-90 behavior behind the current Pylon
6
OpenTUI state model. It uses the shared `@openagentsinc/nip90` package, backed by
7
the workspace `nostr-effect` protocol helpers, instead of rebuilding NIP-90
8
parsing or event construction locally.
7
This page is kept because the code it describes is the port source for
8
[#30](https://openagents.com/OpenAgentsInc/openagents/issues/30), the issue that
9
tracks reviving provider mode. It records what was removed, what survived, and
10
what `pylon provider` actually does now, so nobody spends an afternoon looking
11
for a command that no longer exists.
9 12
10
## Commands
13
## Commands that no longer exist
11 14
12
```sh
13
pylon provider go-online
14
pylon provider once
15
pylon provider go-offline
16
pnpm run smoke:nip90-provider
17
pnpm run provider:serve
18
```
19
20
`go-online` persists lifecycle `online` and adds
21
`capability.public.pylon.nip90.text_inference.v0.3` to the local runtime
22
state. The default dashboard starts the provider loop only when the persisted
23
runtime lifecycle is `online` or `assignment-ready`.
15
Earlier revisions of this page told you to run these. Do not:
24 16
25
`provider once` is the headless smoke entrypoint. It publishes NIP-89 handler
26
info and subscribes once to the configured relays. If the persisted lifecycle is
27
offline, it exits with a public-safe `provider_not_online` reason.
17
| Removed command                 | Status                                                     |
18
| ------------------------------- | ---------------------------------------------------------- |
19
| `pylon provider once`           | Deleted. The CLI catalog offers no `once` subcommand.      |
20
| `pnpm run provider:serve`       | Deleted with `apps/pylon/scripts/nip90-provider-serve.ts`. |
21
| `pnpm run smoke:nip90-provider` | Deleted. No such script is defined.                        |
22
| `pylon provider approve-labor`  | Deleted. See `labor-market-provider-loop.md`.              |
28 23
29
`pnpm run provider:serve` (`scripts/nip90-provider-serve.ts`) is the
30
long-running headless serve entrypoint: it runs the persistent loop against
31
the configured relays with the real Pylon home, the default local Apple FM
32
runtime, and the MDK agent wallet for payment-required quotes
33
(`PYLON_MDK_WALLET_HOME` points the wallet subprocess at a wallet home that
34
can actually create invoices). It only ever issues receive invoices. It
35
never pays.
24
The files behind them went in the same commit: `apps/pylon/src/provider-nip90.ts`,
25
`apps/pylon/src/labor-market.ts`, `apps/pylon/src/multi-earning-ledger.ts`, the
26
Spark wallet stack, and the MDK treasury and tips services.
36 27
37
`pnpm run smoke:nip90-provider` uses a temporary Pylon home and the scoped market
38
relay to prove subscribe, NIP-89 advertise, targeted kind `5050` intake, local
39
runtime execution, kind `7000` feedback, kind `6050` result publication, and
40
redacted local earnings state. It uses fake local runtime/wallet adapters and
41
does not claim paid settlement.
28
## What `pylon provider` does today
42 29
43
## Relay And Runtime
30
`apps/pylon/src/cli-catalog.ts` declares exactly two subcommands:
44 31
45
Default relay:
46
47
```text
48
wss://relay.openagents.com
32
```sh
33
pylon provider go-online
34
pylon provider go-offline
49 35
```
50 36
51
The loop:
52
53
- publishes NIP-89 handler info for kind `5050`.
54
- stays subscribed through quiet periods: an idle relay (no frames for the
55
  60s message window) is treated as keep-waiting, not an error, and any
56
  dropped socket or transient relay/runtime failure logs and resubscribes
57
  after a short delay instead of stopping the service (#4866 root-cause fix
58
  for `[NIP-90] Service stopped with error: ... relay message timed out`).
59
- subscribes to targeted and broad kind `5050` text-inference requests.
60
- rejects malformed, encrypted, wrong-target, stale, missing-bid, underbid,
61
  duplicate, and over-capacity requests before local execution.
62
- executes admitted jobs on the local Apple FM runtime through a
63
  runtime-neutral `ProviderTextRuntime` contract.
64
- publishes NIP-90 kind `7000` states: `payment-required`, `processing`,
65
  `success`, and `error`.
66
- publishes kind `6050` results with `amount` and BOLT 11 data for NIP-90
67
  settlement.
68
- records local earnings as public-safe refs and amounts.
69
70
## Wallet Boundary
71
72
The raw BOLT 11 invoice is allowed only inside Nostr relay events required by
73
NIP-90. Pylon must not write raw invoices, mnemonics, preimages, payment
74
hashes, wallet-home paths, or agent tokens into OpenAgents API payloads, logs,
75
local persisted provider state, issue comments, or commits.
76
77
Persisted earnings include:
78
79
- request event id.
80
- requester pubkey.
81
- amount in msats/sats.
82
- public-safe receipt ref.
83
- result event id.
84
- timestamp.
85
86
They do not include raw invoice or wallet material.
87
88
## Current Evidence Limit
89
90
Unit coverage verifies admission, event construction, and the redaction
91
boundary. A funded buyer end-to-end payment is intentionally left for the
92
buy-mode/funded smoke issues because it moves live sats and requires operator
93
approval.
37
`go-online` is local bookkeeping. It writes runtime lifecycle `online`, probes
38
the Claude, Codex, and Apple Foundation Models readiness this device actually
39
has, records the resulting capability and blocker refs, and reports per-account
40
coding capacity as quantities — available, busy, queued, and ready slots per
41
connected Codex account. It opens no socket, publishes no Nostr event, quotes
42
no job, and earns nothing. `go-offline` reverses the lifecycle.
43
44
Whatever runs on that capacity arrives through the assignment and dispatch
45
paths, not through a market.
46
47
## What survived the removal
48
49
- **The protocol package.** `packages/nip90` was kept and extended through the
50
  retirement. It carries the NIP-LBR lane — `lbr.ts` (request, quote,
51
  acceptance, result), `lbr-closeout.ts` (a content-addressed, public-safe
52
  receipt binding one whole lifecycle), and `lbr-bond.ts`, a post-retirement
53
  addition covering forfeitable provider bonds.
54
- **The presence lane refs.** `packages/pylon-core/src/presence/nip90-lane-refs.ts`
55
  still resolves relay URLs from `PYLON_NIP90_RELAYS` and builds the
56
  `lane.public.nip90.*` refs that `presence.ts` would attach to a registration.
57
  In practice it never fires: `providerDiscoveryFields` only attaches those
58
  fields to a Pylon whose capability refs include
59
  `PYLON_NIP90_PROVIDER_CAPABILITY_REF`, and no code path adds that ref any
60
  more.
61
- **The labor admission rules.** `apps/pylon/src/labor.ts` still holds workspace
62
  bounding, provider-auth exfiltration detection, the public-safe result
63
  projection, and the local agent command builders. Nothing imports it;
64
  `makeConfiguredLaborRuntime` is listed in
65
  `scripts/uncalled-production-symbol-baseline.json`.
66
- **The buyer half of the negotiation.** `packages/sarah/src/lbr-request-quote/`
67
  was recovered on 2026-07-24 as request and quote only.
68
69
## What was revived, and how far
70
71
`openagents provider settle` (`packages/openagents-cli/src/provider-command.ts`
72
and `provider-settlement.ts`, commit `f0c36de6a0`) revives the earning half at
73
the one seam that can be proven offline. It reads a lease and an LBR closeout
74
receipt from files and decides what the job earned.
75
76
An earning requires a receipt that names this job and this provider, was not
77
issued by the provider to itself, carries both a verification command and the
78
evidence that command produced, carries the platform's own closeout, is
79
content-addressable, landed inside the lease window, and prices the job exactly
80
as the lease did. Ten named refusals cover the ways that fails, and each earns
81
zero.
82
83
Nothing moves. A settled decision reports `payout_rail: "not_connected"` and
84
`custody: "none"`. There is no key, no wallet daemon, and no spend authority.
85
86
## Why the rest stays unbuilt
87
88
`INVARIANTS.md` still says that payments, markets, wallet custody, payout,
89
billing credits, and settlement are not part of the accepted MVP, and that any
90
revival requires a fresh owner-approved design, custody model, invariant change,
91
and proof program. `scripts/vp1-retired-money-surface-guard.mjs` enforces it on
92
every push.
93
94
Presence with capacity-as-quantity and the claim, lease, and submit transport
95
also need a market transport the do-not-build register defers. When that lane
96
arrives it takes the relay, provider-daemon, and skeptical-client shape, not a
97
NIP-90 or DVM revival.
98
99
One failure from the original loop is worth carrying forward into any
100
replacement. The live run on 2026-06-12 treated 60 seconds of relay silence as
101
fatal, so every registered provider went dark about a minute after going online.
102
A responder needs idle keepalive and resubscribe, and an idle relay has to be
103
read as keep-waiting rather than as an error.
packages/pylon-core/src/presence/nip90-lane-refs.ts modified +12 -8

@@ -5,14 +5,18 @@

5 5
 * `presence.ts`'s heartbeat needs exactly these four symbols to publish
6 6
 * provider-discovery fields (`PYLON_NIP90_PROVIDER_CAPABILITY_REF`,
7 7
 * `providerNip90LaneRefs`, `relaysFromEnv`) and their supporting constant
8
 * (`OPENAGENTS_MARKET_RELAY_URL`). The rest of `provider-nip90.ts` — job
9
 * request/result handling, the labor-market bridge, and the Apple FM client
10
 * used to *serve* NIP-90 work — depends on `wallet.ts`, `labor-market.ts`,
11
 * and `@openagentsinc/pylon-runtime` and stays in `apps/pylon`. This module
12
 * depends only on the public `@openagentsinc/nip90` job-kind constants, so
13
 * it is a clean leaf for presence to sit on top of. `apps/pylon`'s
14
 * `provider-nip90.ts` re-exports these symbols (rather than redefining them)
15
 * so there is a single source of truth.
8
 * (`OPENAGENTS_MARKET_RELAY_URL`). This module depends only on the public
9
 * `@openagentsinc/nip90` job-kind constants, so it is a clean leaf for
10
 * presence to sit on top of.
11
 *
12
 * The serving half is gone. `apps/pylon/src/provider-nip90.ts`, which used
13
 * to re-export these symbols and handle job requests and results, was
14
 * deleted on 2026-07-14 by `21e82ce829` ("retire money sites and wallet
15
 * authority"), so this file is now the only definition. Nothing adds
16
 * `PYLON_NIP90_PROVIDER_CAPABILITY_REF` to a runtime's capability refs any
17
 * more, which means `providerDiscoveryFields` returns nothing in practice.
18
 * These refs are kept as the port source for issue #30. See
19
 * `apps/pylon/docs/nip90-provider-loop.md`.
16 20
 */
17 21
import {
18 22
  KIND_JOB_LABOR_CODE_TASK,
packages/pylon-core/src/presence/presence.ts modified +13 -9

@@ -938,15 +938,19 @@ async function postJson(

938 938
}
939 939
940 940
// #4864 provider discovery fields, included only for Pylons that have
941
// declared the NIP-90 provider lane (the go-online path adds
942
// PYLON_NIP90_PROVIDER_CAPABILITY_REF). Consent semantics: a provider
943
// that goes online IS announcing publicly — its provider loop publishes
944
// NIP-89 handler info signed with this same pubkey on these same relays.
945
// Carrying pubkey + relay refs + lane refs into the worker registry adds
946
// discoverability for stranger buyers, not exposure. The relay refs are
947
// the values this Pylon actually listens on (relaysFromEnv), never a
948
// worker-side constant, so the #4863 relay-domain cutover follows the
949
// provider configuration automatically.
941
// declared the NIP-90 provider lane. Consent semantics: a provider that
942
// declared that lane IS announcing publicly, so carrying pubkey + relay
943
// refs + lane refs into the worker registry adds discoverability for
944
// stranger buyers, not exposure. The relay refs are the values this Pylon
945
// actually listens on (relaysFromEnv), never a worker-side constant, so
946
// the #4863 relay-domain cutover follows the provider configuration
947
// automatically.
948
//
949
// Inert since 2026-07-14. `21e82ce829` retired the provider loop, and no
950
// code path adds PYLON_NIP90_PROVIDER_CAPABILITY_REF to a runtime's
951
// capability refs any more, so this returns {} in production. It is kept
952
// as the port source for issue #30, whose revival needs an owner-approved
953
// design. See `apps/pylon/docs/nip90-provider-loop.md`.
950 954
export function providerDiscoveryFields(
951 955
  state: PylonLocalState,
952 956
  env: NodeJS.ProcessEnv = process.env,

This page updates live while a promote is in flight · changelog