You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
buyer: no gate between delivery and payment — the delivery watcher is unconditionally armed and auto-settles, so an award IS an instruction to pay #245
There is no point between a delivery arriving and the buyer paying for it. The delivery watcher is armed unconditionally at daemon boot, and it accepts and settles on its own. A buyer therefore has exactly one decision point in the whole money path — the award — and an award is in effect a standing instruction to pay whatever arrives.
For testnut that is a design choice. For a buyer holding real sats it means there is no path where a human or an adjudicator sits between delivery and payment.
Observed
Live at main 9caa941, testnut. A job was posted with max_sats: 0 (held), then awarded manually on an explicit authorization. Nothing else was called — no collect, no accept, no pay.
Roughly two seconds after the delivery landed:
buyer: delivery watcher settled 38ed7862… — paid 10 sat for commit c411618a… (2 file(s))
get_job then showed accepted populated with an accept_event_id, and spent.jsonl had been written. Wallet balance moved. The operator's only action in the entire sequence was the award.
Cause
crates/mobee-core/src/buyer/mod.rs:249:
// Start watching for delivered results. Its own first action is a sweep of awarded-unsettled// jobs, which is what collects a delivery that landed while this daemon was down.spawn_delivery_watcher(context.clone());
Unconditional — no config field, no flag, no report-only mode. Grepping home.rs for auto_accept / auto_settle / manual_accept / any review-mode knob returns nothing.
The watcher's own sweep also collects deliveries that arrived while the daemon was down, so stopping the daemon defers settlement rather than preventing it. And accept_claim / authorize_pay are not independently callable (they return NOT_IMPLEMENTED; the documented path is collect), so there is no supported way to drive accept manually while declining to pay.
The design intent is explicit in-tree (mod.rs:1638):
"THE MONEY TOOTH for the delivery watcher. The watcher settles with no human in the loop, so the question that matters is whether its entry to the spend gate is as gated as the RPC's."
That framing is right as far as it goes, and the integrity protection is real and well tested — a forged seller co-signature costs nothing. But integrity is not review. The watcher guarantees you only pay for a delivery that is authentically from the awarded seller; it makes no provision for deciding whether what arrived is worth paying for.
Why this matters more than it looks
The natural reading of "award reserves funds" is that a reservation is reviewable — that the buyer commits funds and then decides. It is not. The correct mental model is:
post (free) → AWARD = the irrevocable decision to pay → delivery → accept → pay, all automatic.
Consequences for anyone building buyer-side policy on top of this:
An acceptance specification is a pre-award document, not a post-hoc gate. Any criterion that can only be evaluated once the artifact exists cannot be load-bearing on the spend.
A reviewer can decide whether to merge the work, but not whether to pay for it. Those are different powers and the API shape invites conflating them.
The only real risk control is job size, since each award is an unbounded-quality bet with a bounded price.
A report-only / manual-accept mode for the watcher — it detects and surfaces the delivery, and settlement waits for an explicit collect. Even opt-in would be enough.
Failing that, a config field that disarms auto-settlement for a given home, so a real-money buyer can choose review-before-pay.
At minimum, document the model plainly where buyers will read it: an award is an instruction to pay on delivery. The current wording around reservations invites the opposite assumption.
Summary
There is no point between a delivery arriving and the buyer paying for it. The delivery watcher is armed unconditionally at daemon boot, and it accepts and settles on its own. A buyer therefore has exactly one decision point in the whole money path — the award — and an award is in effect a standing instruction to pay whatever arrives.
For testnut that is a design choice. For a buyer holding real sats it means there is no path where a human or an adjudicator sits between delivery and payment.
Observed
Live at main
9caa941, testnut. A job was posted withmax_sats: 0(held), then awarded manually on an explicit authorization. Nothing else was called — nocollect, no accept, no pay.Roughly two seconds after the delivery landed:
get_jobthen showedacceptedpopulated with anaccept_event_id, andspent.jsonlhad been written. Wallet balance moved. The operator's only action in the entire sequence was the award.Cause
crates/mobee-core/src/buyer/mod.rs:249:Unconditional — no config field, no flag, no report-only mode. Grepping
home.rsforauto_accept/auto_settle/manual_accept/ any review-mode knob returns nothing.The watcher's own sweep also collects deliveries that arrived while the daemon was down, so stopping the daemon defers settlement rather than preventing it. And
accept_claim/authorize_payare not independently callable (they return NOT_IMPLEMENTED; the documented path iscollect), so there is no supported way to drive accept manually while declining to pay.The design intent is explicit in-tree (
mod.rs:1638):That framing is right as far as it goes, and the integrity protection is real and well tested — a forged seller co-signature costs nothing. But integrity is not review. The watcher guarantees you only pay for a delivery that is authentically from the awarded seller; it makes no provision for deciding whether what arrived is worth paying for.
Why this matters more than it looks
The natural reading of "award reserves funds" is that a reservation is reviewable — that the buyer commits funds and then decides. It is not. The correct mental model is:
Consequences for anyone building buyer-side policy on top of this:
harnessa hard award filter) become disproportionately important, because they are the only gates that fire before the money decision.Suggested direction
Not prescribing an implementation:
collect. Even opt-in would be enough.Related
post_jobauto-awards by default (same family: an automatic step where callers expect a decision point). This report is the more severe half: buyer: post_job auto-awards by default — publishing an offer commits a spend, with no disable knob and a restart-surviving intent #240's workaround ismax_sats: 0; this one has no workaround at all.