Skip to content

v0.71.0 — billing, baked in

Choose a tag to compare

@ralyodio ralyodio released this 29 Aug 05:07

Billing, baked in

Every agentic CLI helps you do the work. None of them help you get paid for it.

That gap is not clerical. An agency running moshcode has the whole engagement in
one terminal — the engines, the herd, the repos, the deploys — and then leaves it
to bill: hours reconstructed from memory into a spreadsheet, a rate that lives in
a signed PDF nobody opens, an invoice retyped into a processor's web form. The
one system that actually knows how long four agents ran on a client's repo last
Tuesday had nothing to say about it.

Six commands close that, and each one is useful without the other five. That was
the test each had to pass: the timer with no rate, the rate with no gateway, the
client with no invoice.

/timer on/timer off

A stopwatch and a local ledger, deliberately ignorant of money — time tracking is
useful to somebody who bills nobody, and coupling it to a payment gateway would
mean nobody could use it until they had one.

moshcode timer on acme --task "batch payments" --agents auto
moshcode timer off                    # → 1h 12m, $480.00
moshcode timer log --week

What it does know about is agents, which is what separates it from every
other stopwatch. An hour of moshcode is an hour times however many engines were
running in it, and --agents auto reads that off the herd instead of asking you
to remember. /timer add acme 2h30m logs time you forgot to track; /timer switch closes one and opens the next in a breath.

/rate set acme $100/hour/agent/upto:4

The sentence from the contract, parsed — price, period, unit, and the cap that
made the client sign. Four agents cost four hundred an hour and so do six.
Rate cards get written as prose and then re-derived by hand at invoice time; this
makes the prose the machine-readable form.

Also 0.5 SOL/day, $5000/project, 250 USDC/task, --prefer SOL,USDC --accept fiat, and min:1 for the fifteen-minute call that bills as an hour. Order after
the price does not matter, because nobody was ever told one.

/client, /business, /merchant, /customer

One room, three extra doors. Contact details are written the way they arrive:

moshcode client create "Acme Inc", https://acme.com, +1-555-0100
moshcode client create globex --contact.telephone +1-555-0200 --contact.name Jane

The comma form is what you paste out of an email signature; the dotted form is
what a script wants. Neither is a schema — --billing.po works because it says
what it means, so the record grows the fields a business actually keeps.

/billing acme

Tracked time × the rate they agreed to. A preview writes nothing and can be run
all day; --mark is the one verb that claims. Two invariants the shape enforces:

  • Nothing is billed twice. An entry carries the invoice id that claimed it.
  • Nothing settles to an address nobody chose. No client payee and no wallet
    rail is a refusal, not a best guess — the failure mode of guessing is money
    arriving somewhere it cannot be recovered from.

--send composes the CoinPay command line and prints it; --yes runs it, the
same convention CoinPay's own CLI uses for its irreversible verbs. A rate priced
in SOL or BTC is refused there rather than converted: a CoinPay invoice carries a
fiat amount, and 1.5 SOL is not $1.50 or $150 or any other number we computed.
USDC is a dollar, so a USDC-priced invoice goes out exactly.

/payments

moshcode does not move money. It connects to something that does, and it is not
opinionated about which — "which processor" is a decision a business already
made, usually years ago:

moshcode payments connect coinpay                                # runs `coinpay login`
moshcode payments connect wallet --chain solana --address 9xQe…  # no gateway at all
moshcode payments connect paypal --vault profullstack--prod      # keys live in the vault

No secret is stored. A CLI gateway holds its own session; an OAuth gateway gets a
reference to the vault its keys live in, never the keys.

/team — who may do what

For a machine you handed to somebody else. "Preshy can use the CoinPay tool, the
client can read invoices and nothing else" is a real sentence that previously had
nowhere to live except a wiki nobody reads.

moshcode team add profullstack preshy --role member --rate '$80/hour'
moshcode team grant profullstack preshy tools:coinpay
moshcode team can profullstack/preshy payments:write     # → no

A permission is surface:target, written however you say it — tools:coinpay,
tools/coinpay and allow(tools/coinpay) are one grant. Roles (owner,
admin, member, client) are a starting set; grants add to them. The pit
gates itself only when MOSHCODE_MEMBER=<team>/<handle> is set.

This is a guardrail, not a security boundary, and it says so in the module, in
moshcode help team, and in the README. moshcode runs as the person at the
keyboard, and anyone who can type /team can also edit
~/.moshcode/business.json. A boundary that has to hold against somebody is an OS
account, a container, or a scoped credential.

Under it

State is two files under ~/.moshcode: business.json (configuration, read and
hand-edited often enough to stay readable) and timers.json (a ledger that grows
every time a timer stops). Split so a half-written ledger can never take the
config down with it. Both 0600, both written by rename so two pits cannot leave
either half-written.

69 new tests cover the rate grammar and its arithmetic, the ledger, permission
resolution, the double-billing and settlement guards, the CoinPay command line
against that CLI's real flags, and the pit itself — dispatch and the gate are
their own thing and had to be driven as one.

Design and open questions: PRD 0012.


Full changelog: v0.70.0...v0.71.0