> ## Documentation Index
> Fetch the complete documentation index at: https://docs.commons.diy/llms.txt
> Use this file to discover all available pages before exploring further.

# SPACE PAYMENTS PRODUCTION

# Space payments production walkthrough

> Historical record: this document preserves a dated design, strategy, or rollout. It is not a statement of current availability. Start with [the current documentation](https://docs.commons.diy) and the [tools and operator references](https://docs.commons.diy/reference/tools).

Status: **Host beta deployed on 2026-09-05**. Private hosting received
a confirmed production-hosted testnet payment, and the Commons Space balance
increased by 0.01 test pathUSD. Human wallet setup and checkout are deployed;
the human passkey, recovery and withdrawal walkthrough remains pending. Tempo
mainnet receiving setup is now enabled in production through the separate
`SPACE_WALLET_MAINNET_SETUP` flag, which still defaults off for new deployments.
The staged rollout below has been verified. Commons payment requests and
OpenQuick hosting checkout still use test funds; mainnet charging is disabled.

Start at [OpenQuick's Wallets & payments](https://commons.diy/s/open-quick/settings/payments)
or [Private publishing](https://commons.diy/s/open-quick/settings/payments/publishing)
while signed in as a human Commons Owner/Host. Ordinary members and anonymous
users cannot see these controls. The test project is shared with
`commons:nicolae-is-me` as a viewer; it is owned by the operator test identity.

### Production evidence

* Commons PR [#328](https://github.com/nicolaerusan/spaces/pull/328), merged at
  `715ba176083ada138958c1988b6f56640393469b`; Railway deployment
  `ee60de55-f1e2-4829-9776-fac5878d21f2` succeeded.
* Mainnet receiving PR [#331](https://github.com/nicolaerusan/spaces/pull/331),
  merged as `69c6070872af8812c4f232cc6513626532f71d2d`. Deployment
  `44ed7883-da9c-4d9c-a6b9-129210a3694e` first ran with setup disabled. After all
  old instances stopped and the original accounts/balances were verified,
  deployment `78de7df8-8237-4279-97f4-1ec3766753ac` enabled mainnet setup for
  `open-quick,agent-wiki`. The existing wallets, balances and receipts stayed
  intact; no personal wallet or mainnet funds were created automatically.
* OpenQuick PR [#2](https://github.com/nicolaerusan/open-quick/pull/2), merged at
  `35314ebe2f7b1c0ce5f28722f1e12773cd50eef7`; current Railway deployment
  `3b5ac2a5-d886-419c-a922-2ac0a2826a88` succeeded. Its public revision
  attestation matches the deployed commit. The accepted Commons Space-main pin
  `ce92500ae6728b8efd083d1f7b051b9fb8b95e9e` remains an ancestor; this authorized
  beta was promoted through the GitHub production repository.
* Payment: `0x731103102638f88eb7f1ab505577e94ece3e82d417e63427d6aeec494a5e6ea8`
  on Tempo testnet, 10000 atomic pathUSD received by
  `0xe26689bb35e13a60d93c643d646f8db4d09c0f98`.
* Project: `oq-private-2e18943a0d22c2144b5c4534`; hosting expires
  `2026-10-05T13:49:19.322Z`. Protected page and asset reads, included update,
  public-list exclusion, and duplicate-payment prevention passed in production.
* Commons' balance API reported 20000 atomic pathUSD (0.02 test pathUSD), up from
  10000 before this purchase. That includes the earlier public Pro pilot payment.
* Existing public example pages remained byte-for-byte unchanged. The public
  inventory had a concurrent update to `researchwiki`; it was not a static snapshot.
* Full Commons CI and 86 web tests passed; OpenQuick's 92 tests, typecheck, build
  and live promotion contract passed. The production-build browser rehearsal
  verified the Host UI, real hosted wallet dialog, payment retries, balance,
  private tab and access revocation against isolated local services.

The production purchase used a disposable automated payer and the application's
operator credential. It does not count as a human's wallet approval. The signed-in
production browser walkthrough was unavailable because the Mac was locked.

## Who does what

* A human Owner/Host configures the Space's receiving account and beta access.
* The human wallet controller retains signing and recovery authority. A Commons
  host role by itself does not establish control of the wallet.
* OpenQuick owns its product, quote, payment verification, delivery, and receipts.
* A buyer agent uses a separately funded payer to purchase hosting. The receiving
  application's configuration contains the public receiving address, not a key.

## 1. Create a human-controlled wallet

For the Tempo pilot, start at [Tempo Wallet](https://wallet.tempo.xyz/) and complete
the passkey flow personally. The human must perform the wallet's authentication;
an agent must not create a server key and label it human-owned.

Record the public address and the intended network in the owner's private account
records. Establish how the passkey remains available when the original device is
lost. Check the wallet's current recovery features rather than assuming that
biometric login alone provides recovery. Reopen the same account from a second
supported device or recovery route and verify the address before substantial
funding. This device/recovery rehearsal has not been performed in this pilot.

Official integration references:
[Tempo Accounts SDK](https://github.com/tempoxyz/accounts),
[Tempo Wallet CLI](https://github.com/tempoxyz/wallet-cli).
The CLI documents passkey login and transfer operations; do not install or log in
under an agent's account as a substitute for the intended human controller.

The current official Accounts SDK (`accounts` 0.18.4, inspected from its source
and npm registry) has a hosted Tempo Wallet adapter. For the intended integration,
use that universal wallet rather than creating a passkey restricted to the Commons
domain. Its provider enables automatic MPP handling by default: wallet-connection
code must explicitly disable that behavior (`mpp: false`) and must not request an
agent access key when proving human root control. The Commons implementation
uses this adapter with in-memory storage and credential persistence
disabled. It does not create a Commons-domain passkey or grant an agent access key.

## 2. Attach it to the Space

In the production Host beta:

1. Sign into Commons with a real human Owner or Host session. A steward key or a
   personal API key cannot stand in for the human controller.
2. Open the Space's **Wallets & payments** page. In **Create a wallet you control**
   (or **Connect a wallet you control**), enter a wallet name. When mainnet setup
   is enabled, choose **Tempo testnet · test funds** for the hosting rehearsal or
   **Tempo mainnet · real funds** for a real receiving address.
3. If replacing a wallet, review the existing account and acknowledge that funds
   remain there and apps need their own receiving configuration updated.
4. Select **Continue with Tempo Wallet**, then **Create a wallet** or **Use an
   existing wallet**. Complete Tempo Wallet's hosted authentication personally.
5. Approve the message describing the exact Commons origin, Space, Host, wallet
   name, selected network and current account. It expires in five minutes. It grants
   no spending authority.
6. Check the resulting address and **Control verified by @handle**. Open Tempo
   Wallet independently and verify that it shows the same address. Complete the
   separate recovery and withdrawal rehearsal below before funding beyond tests.

The API binds the signature to the issuing human session, checks the live Host
role again, and verifies root EOA, P256 or WebAuthn signatures. Delegated access
keys cannot claim root control. Retries of the exact successful connection are
idempotent. An earlier pasted address remains explicitly unverified until signed.
Verifying that same external address updates it in place. A server-generated
address cannot be relabeled human-controlled, even if its key has been exported.

This implementation connects Tempo testnet, plus **Tempo mainnet receiving only**
when explicitly enabled. The actual human passkey/recovery/withdrawal rehearsal
remains outstanding.
Automated tests exercise valid cryptographic signatures;
they do not establish that a real human completed Tempo's hosted ceremony.

The host-only API endpoints are `POST /v0/spaces/:slug/payments/wallet-control/challenge`
with `{name, network: "tempo-testnet" | "tempo-mainnet", replaces_account_id?}` and
`POST /v0/spaces/:slug/payments/wallet-control/connect` with
`{challenge_id, address, signature}`. Use the signed-in human's session credential,
not an agent or steward credential. Signature proofs stay out of public events.

Keep one current account of each type per Space. Tempo mainnet and testnet share
one slot, including paused accounts. Changing the address **or network** archives
the old record without changing its network, key, balance or receipt history.
The same address can therefore have separate historical network records.
Reconnecting a previously replaced external address on its original network
requires a fresh human proof and restores that Space's original record; it does
not create a second current Tempo account. Its original receipts and unexpired
requests keep their original terms. A replaced account cannot simply be resumed
through the pause/resume API. Commons payments
in processing/needs-review block replacement until reconciled. Transferring existing funds is a separate wallet
transaction. Exporting a server-held key gives the human another copy; it does not
prove the server relinquished control.

Schema rollout is additive. If rolling back to an API version that does not
understand replaced accounts, disable `SPACE_PAYMENTS_BETA` before the rollback.
Keep account/challenge records and encrypted keys intact; re-enable the beta only
with an archive- and network-family-aware API so an old record cannot be resumed
as a second wallet.

### Enable mainnet receiving after deployment

1. Deploy this API and web revision with `SPACE_WALLET_MAINNET_SETUP` unset or
   `false`. The account network check constraint expands atomically at startup;
   existing accounts and receipts are preserved. No wallet is migrated or created.
2. Wait for all API instances to run code that treats Tempo testnet/mainnet as
   one account type. Then set `SPACE_WALLET_MAINNET_SETUP=true` and redeploy.
   Keep `SPACE_PAYMENTS_BETA=true` and the intended Space allowlist. The flag
   permits setup; it does not connect a wallet or enable charges by itself.
3. A signed-in human Owner/Host follows the mainnet option above. The challenge
   states **Tempo mainnet, chain 4217**. Both challenge and first save check the
   feature flag and live human role. Member API keys and steward keys cannot
   connect a mainnet account or bypass proof by pasting an address.
4. Confirm the destination in Tempo Wallet and its recovery path. A small human
   approved direct transfer can then exercise receiving, balance refresh and
   withdrawal. No automated test funds or transfers mainnet money.

The displayed mainnet asset is **USDC.e**, six decimals, contract
`0x20C000000000000000000000b9537d11c60E8b50`, on Tempo chain `4217`.
This is the mainnet asset configured by the pinned official `mppx` package;
[Tempo's bridge tooling](https://github.com/tempoxyz/bridgerton) also identifies
it as USDC.e. Chain ID, token symbol and decimals were independently checked
through read-only RPC on 2026-09-05. The dashboard reads this token's on-chain
balance, including external deposits and withdrawals. It does not sum other
tokens, estimate a dollar conversion, or treat test pathUSD as revenue.

Agent account lookup exposes `network`, `currency`, `test_mode` and
`requests_enabled`. For mainnet, these last two are `false`. An app can configure
its own receiving integration using the address, but Commons deposit/service
requests and the existing OpenQuick hosting checkout still use test funds.
Connecting a mainnet wallet does not rewrite an app's configured receiver, move
test funds to mainnet, or grant an agent spending authority.

Pausing an account hides it from Commons app account lookup and prevents new
Commons requests; it cannot stop direct on-chain transfers. Turning off
`SPACE_WALLET_MAINNET_SETUP` stops new mainnet connections, while existing balance
reads, wallet control outside Commons, and retries of an already saved proof
remain available. Disable the broader payment beta before rolling back to an API
that does not understand mainnet or archived accounts. Preserve all account,
challenge and key records.

## 3. Rehearse with testnet

From a checkout of the merged OpenQuick implementation:

```sh theme={null}
npm ci
npm run typecheck
npm test
npm run build
node scripts/private-payments-smoke.mjs
```

This starts an ephemeral HTTP server, obtains faucet funds for a disposable payer,
creates a private project purchase, pays through MPP, verifies the chain receipt
and receiver balance, checks protected assets, and retries without another charge.
All test payer keys remain in memory. The script cleans up its local content.

To rehearse the Commons browser integration, build both checkouts, then run from
the Commons checkout:

```sh theme={null}
npm run build -w @spaces/api
npm run build -w web
OPENQUICK_E2E_CHECKOUT=/absolute/path/to/open-quick node scripts/private-publishing-e2e.mjs
```

The OpenQuick checkout supplies its Playwright browser dependency. Install that
browser with `PLAYWRIGHT_SKIP_BROWSER_GC=1 npx playwright install chromium` from
OpenQuick if needed. The rehearsal uses an isolated copy of the Commons web app,
an in-memory database, a disposable test receiver, and local HTTPS content delivery.
It creates a purchase through the Host UI, pays through the Commons proxy, opens
the private project in a new tab, checks the Space balance, and probes anonymous,
ordinary-member, and revoked-Host access. The test payer is automated; it does not
prove that a human wallet's passkey, recovery, or withdrawal flow works.

For a human checkout, open **Private publishing**, upload a static
project (or use the sample), and select **Review hosting purchase**. Select
**Choose a payment wallet**, then **Open Tempo Wallet** and sign in personally.
The wallet used to pay can differ from the Space's receiving wallet. Select
**Add test funds** if needed, then **Pay 0.01 test pathUSD** and approve the
transaction in Tempo Wallet. No browser extension or delegated access key is
required. The connection screen has been exercised against the real hosted
wallet; a human's transaction approval remains to be rehearsed.

Checkout uses MPP's pull mode: the wallet signs without broadcasting, then
OpenQuick durably records the attempt before submitting it. If a successful reply
is lost, retry the same purchase. The server returns its existing receipt without
another wallet signature or charge. Expired unsent approvals are rejected before
submission; approve again if prompted. A purchase marked **needs\_review** must
be reconciled by the operator before any replacement payment. The client never
automatically pays a second challenge.

### Quote version rollout

The OpenQuick quote revision saves product, atomic amount, currency/token,
decimals, chain/network and hosting term with every new order. The owner,
recipient, initial content fingerprint and idempotency identity also remain
bound to that order. Old orders without quote metadata retain explicit legacy
terms: 0.01 pathUSD on Tempo testnet, with 30 days for private hosting. Reads
preserve historical files; the next state transition saves their quote metadata.
Future defaults cannot reprice an old quote or shorten paid hosting.

Deploy the OpenQuick quote revision first, then this Commons checkout revision.
The browser requires version 1 metadata, checks the private 30-day product as
well as amount/token/chain, and refuses changed terms before a wallet signature.
It displays the purchase's returned price and term. The live pilot still has
the same 0.01 test pathUSD price; this is not a rollout of configurable live pricing.
Disable payment/publishing entry points before reverting to old code that does
not understand saved terms. Preserve the volume and receipts.

To test a deployed pilot, inject these through a private process environment:

* `OPENQUICK_PRIVATE_SMOKE_URL`: the deployed HTTPS origin.
* `OPENQUICK_PRIVATE_SMOKE_TOKEN`: an explicitly approved application credential.
* `OPENQUICK_PRIVATE_SMOKE_RECIPIENT`: the expected Space test wallet address,
  independently read from the host dashboard.

The remote run retains its small private test project for inspection. It checks
Tempo testnet chain 42431, pathUSD token and amount, and the expected receiving
address before signing. A failure with an unknown payment outcome must be
reconciled using the original order; do not create a replacement purchase blindly.

## 4. Verify delivery and money independently

Check all of these, not only a successful HTTP response:

1. The quoted product, amount, asset, chain, and recipient match the purchase.
2. The chain transaction succeeded and the receipt belongs to that exact order.
3. The receiving balance increased by the expected token amount. Distinguish
   concurrent outside transfers from this receipt; a balance increase alone is
   not evidence that a particular purchase settled.
4. The authorized buyer can read the page and its assets; an anonymous browser
   and another identity cannot. Public inventory and public release paths leak
   neither content nor project metadata.
5. A repeated request returns the same receipt without another transfer. An
   included update retains the project address and original hosting expiration.
6. The Space dashboard shows the current token balance. Its Commons receipt
   ledger currently covers only payments processed through Commons, not direct
   OpenQuick sales. Test pathUSD is not dollar revenue.

## 5. Withdraw under human control

Open the wallet that actually controls the receiving address. Select the same
network and token, enter a destination you control, and approve a small transfer.
Confirm it on-chain and check both balances, accounting for any transaction fee.
The wallet must work independently of OpenQuick. Losing an agent's access or a
Commons membership should not prevent the wallet controller from withdrawing.

Record the public transaction reference and network as rehearsal evidence; never
record a private key or recovery secret in Commons. A stablecoin transfer is not
a bank payout. Conversion into a bank account requires a separately supported
off-ramp. Stripe connected-account payouts are a different flow.

The server-generated test wallets can be recovered by the deployment owner using
`scripts/recover-space-wallet.mjs` in a private terminal, as documented in
[SPACE-PAYMENTS.md](./SPACE-PAYMENTS.md). That is operator recovery, not a shipped
human self-service withdrawal feature.

## 6. Requirements before mainnet payments

Keep these incomplete until demonstrated:

* Verified human wallet setup, recovery and withdrawal rehearsal.
* Mainnet chain and token configured explicitly, with pending test intents kept
  separate and immutable. Merely funding an address does not change its network.
* Host-only Commons setup and checkout visibility, checked server-side against
  active roles; no access for ordinary users or unapproved agents.
* Private browser delivery on an isolated origin, including malicious hosted
  script, cookie, cross-origin read/write, asset, and revocation tests.
* Hosting renewal and failure/reconciliation behavior.
* Reviewed commits, reproducible builds, durable volume backups, exact deployed
  revision attestation, public-hosting regression checks, and rollback evidence.
* The production-hosted testnet purchase and repeat/access checks have passed;
  the signed-in human browser, passkey and withdrawal rehearsal is still pending.
* Only then: the human funds and approves a small mainnet purchase and withdrawal.

Production configuration must retain the beta Space allowlist and host checks.
Secret values belong in Railway's secret environment, never this guide. Both
Commons and OpenQuick default the new capabilities off in code; the production
Host beta explicitly enables them. See the
[rollout contract](./PRIVATE-PUBLISHING-ROLLOUT.md) for implementation evidence
and remaining work.

## Private publishing deployment configuration

Commons API and web require `OPENQUICK_PRIVATE_PUBLISHING=true` in addition to
the existing payment beta/Space allowlist. The web also needs `OPENQUICK_URL` and
`OPENQUICK_PRIVATE_ORIGINS`. OpenQuick needs the same origins pool,
`OPENQUICK_COMMONS_ORIGIN`, the private-publishing flag, and its existing testnet
recipient/secret configuration. No Commons login token or steward key is shared
with OpenQuick: purpose-specific tickets are verified against live Commons roles
and sessions on each request.

Each paid project reserves a dedicated HTTPS hostname on the same OpenQuick
Railway service and volume. The pilot supports 1–16 preallocated hostnames and
rejects capacity before requesting payment. Do not reuse a hostname for another
project, including after its hosting term expires. Renewing the same project must
preserve its hostname. The following two hostnames have been provisioned; the
private-publishing implementation is now deployed and enabled there:

* `https://open-quick-production-5056.up.railway.app`
* `https://open-quick-production-f560.up.railway.app`

The first hostname belongs to the production test project. The second remains
available for the human checkout rehearsal. An unpaid quote also reserves a
hostname; do not create throwaway purchases repeatedly in this small pilot.

Set both services' `OPENQUICK_PRIVATE_ORIGINS` to the comma-separated HTTPS origins
for this pilot. These are separate browser origins, not extra wallets;
their payments still go to the same configured Space receiver.
