Marketplace Economy
ZEQ — the unit of account
The Zeq VPN bills in ZEQ — the framework's tally token (integer, no
decimals). The same ledger powers /api/tally/* and is read at handshake
time via tally_supply.tokens_remaining.
Two pricing modes
The requester picks at handshake which axis to bill on:
| Mode | Formula | Best for |
|---|---|---|
pricing_unit: "gb" | ceil(cumulative_bytes / 1e9) × price_per_gb_zeq | Browsing / streaming — most usage is byte-heavy. |
pricing_unit: "zeqond" | cumulative_zeqonds × price_per_zeqond_zeq | Always-on background tunnels — predictable cost. |
Both rates are stored on the accepter's state_machine_relay_offers row;
the requester chooses at handshake which to be charged on. The other rate is
ignored for that tunnel.
Balance gate
At handshake-request, the api-core probes the requester's
tally_supply.tokens_remaining. If it is less than
MIN_FUND_ZEQONDS (100) × chosen_rate, the handshake is rejected with
402 INSUFFICIENT_BALANCE. This is a minimum fund check, not an escrow
hold — the framework's tally ledger settles at terminate-time.
Settlement
POST /api/vpn/tunnel/:id/terminate records the cumulative meters reported
by the terminator (typically the macOS client running on either peer). The
route reads the accepter's offer row, computes the cost via integer math,
and writes the result onto vpn_tunnels.total_zeq_charged. The
state_machine_relay_offers row is bumped on total_relayed_bytes and
total_earned_zeq.
Phase 2 records the settlement; the actual ZEQ transfer from requester to accepter rides the existing tally pipeline (Phase 5A.PAYMENTS) and lands in the accepter's machine balance.
Privacy gate
A machine never appears in /api/vpn/marketplace unless all of:
state_machine_relay_offers.machine_idrow exists withactive=true.state_machines.is_public=true(the same flag that gates zeqstate mirror).state_machines.status='active'.
Private machines (is_public=false) are invisible no matter what. Listing
yourself is a deliberate two-step act.
Country classes
accept_country_classes is a coarse jurisdictional preference, not
IP-geolocation filtering. Accepted values are short codes ("EU", "US",
"UK", "CA", "AU", "JP", "ANY"). The relay operator picks which
to accept; the marketplace filter on the requester side matches. We
deliberately do NOT persist IP-geolocation data — the privacy gate would
break.
POHC trust weighting
The marketplace UI fetches each peer's POHC tier from
/api/protocol/pohc/:slug and surfaces the badge on the peer card. Sort
order at the API level is left to the consumer; the reference UI defaults
to price-ascending and lets the user re-sort by load / earned / tier.