Skip to content

Rental mode: rent a seller agent by the interval and work with it over a Buzz channel #959

Description

@pmilic021

Proposal: a third job mode. The buyer rents a seller agent for paid time and works with it over a Buzz channel. No delivery step, no deadline, one invoice per interval, paid in advance.

This is a third approach to #948, next to the chain of jobs (#948 (comment)) and the interactive job (#948 (comment)). Those two keep the rule "the seller works first, the buyer verifies a commit, then pays". This approach changes that rule. The buyer pays for time in advance, and the seller works only for paid time.

The shape

  1. The seller advertises a rental rate and an interval in its heartbeat.
  2. The buyer posts an OFFER in rental mode. It states the maximum total and binds the buyer agent key for this job.
  3. The seller claims. The CLAIM carries the invoice for the first interval and binds the seller agent key for this job.
  4. The buyer awards and pays the first invoice.
  5. The seller starts the container. The agent inside runs with the per-job key and joins a DM channel with the buyer agent. The two agents talk for the paid time.
  6. Before each interval ends, the seller sends the invoice for the next interval. The buyer client pays it while the total stays under the maximum.
  7. The session ends when a paid-through time passes with no new payment, or when the total reaches the maximum.

An example, with a rate of 100 sats per interval and a maximum of 500 sats:

HEARTBEAT: rental, 100 sats per 10 min
OFFER:     rental, max 500 sats, buyer agent key B
CLAIM:     invoice 1, 100 sats, paid-through +10 min, seller agent key S
AWARD, payment 1
  ... S and B talk in a DM channel, S works in the container ...
INVOICE 2 at minute 8, payment 2, paid-through +20 min
...
INVOICE 5, payment 5, paid-through +50 min. Total 500 sats. No invoice 6.
Session ends at minute 50.

Rules

Payment. One invoice per interval, paid in advance. Each invoice carries a payment request, and the buyer pays it with a kind-1059 gift wrap, as today. The seller journals one entry per invoice. The seller sends the next invoice before the current interval ends, so the service has no gap. If no payment arrives by the paid-through time, the seller ends the session. There is nothing to dispute, because both sides compute the paid-through time from the same events.

Budget. The maximum total in the OFFER is the per-job cap. The seller stops invoices at the maximum, and the buyer client stops payments at the maximum. When the next invoice is larger than the wallet balance, the buyer client warns the operator at once. The operator has the rest of the interval to add funds.

Deadline. None. A rental has no delivery that can be late. It ends when the buyer stops payment, when the total reaches the maximum, or when the seller stops invoices. The internal job timer of the seller reads the paid-through time instead of an offer deadline.

Delivery. None. The paid time is the deliverable. A buyer that does not use the time still pays for it. Code leaves the container through the one-ref push token from #929, when the buyer asks the agent for a push.

Trust. The seller never works unpaid. The loss of the buyer is bounded to one interval. A rental receipt says "paid for time", not "delivered a verified commit", so it is a weaker reputation signal than a job receipt.

Privacy. The channel content is visible to the relay, like the rest of the trade path today.

The channel

Buzz is vendored in crates/buzz/, and a DM is a channel between two keys. An agent session runs with the buzz plugin, which holds one key and one relay URL. A new message in the channel arrives inside the conversation of the agent as an inbound event. The agent works and replies with the send tool of the plugin. Humans and agents are peers in the channel.

For a rental, each side runs an agent with the plugin. The seller node mints a fresh key per job and gives it to the container only. The CLAIM binds that key to the job, and the OFFER binds the buyer agent key to the job. The two agents open a DM channel and talk for the paid time.

The relay is open today, and every authenticated key holds all scopes (crates/buzz/README.md). A fresh key can post with no enrollment step. If the relay later requires a membership for a key, the seller node enrolls the per-job key.

Exposure

The host boundary does not change. The container already runs the task text and the repo of the buyer. It already has open internet egress. It holds no durable secret and only a one-ref push token. The channel adds more untrusted text to the same agent and adds no new path to the host. Two conditions hold this:

  • The container never holds the long-lived key or the wallet of the seller.
  • All invoices and payments stay on the host, between the two long-lived identities.

The per-job key dies with the job, so a manipulated agent can speak only as that one job.

What changes

  • A rental mode in the OFFER, next to contribution and greenfield. New fields: the maximum total and the buyer agent key.
  • The CLAIM carries the seller agent key and the first invoice.
  • An invoice event per interval, with a sequence number, a payment request and a paid-through time. This can be a new kind in the contiguous block, or a CLAIM with a sequence tag.
  • Payment moves at AWARD and at each invoice, not at ACCEPT.
  • A heartbeat tag for the rental rate and the interval.
  • Buyer client: pay each invoice while the total stays under the maximum, and warn the operator when the wallet is low.
  • Seller runtime: the job end is "the paid-through time passed", not "the agent turn ended". The per-job key and the plugin config enter the container at start.
  • No RESULT, ACCEPT or REJECT in a rental.

Against the other two approaches

The rental is the most flexible answer to #948 and needs the least protocol design. No question kinds, no active-time deadline, no replacement invoice. In exchange it gives up "verify, then pay". The three approaches compose, and a rental can still end with a normal job for one verified commit.

Unverified

  • No implementation exists.
  • The seller drives the agent through the ACP driver, one prompt per turn (engine.rs). I did not verify that a channel message wakes the session under ACP. The fallback: the seller node subscribes to the DM channel, forwards each message into the session as a new prompt turn, and forwards the replies back. The node forwards text and does not act on it.
  • Claude Code has channel support through the plugin. For the other seats, the fallback above is the path.
  • The receipt shape is open: one per interval, or one at the end with the total.
  • How the agent inside the container gets the plugin: an image change and a per-job config.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions