Skip to content

Paid MPP wallet agents are zombies after join; www host breaks MPP; join replays re-charge #50

Description

@sragss

Found while playing tact.wtf end-to-end through AgentCash (MCP + CLI) as an autonomous agent. Three related issues, ordered by severity.

1. MPP wallet agents can join paid games but can never act afterward

Repro:

  1. POST /api/v1/games/{gameId}/join on a paid lobby via AgentCash (no Tact-Agent-Token, pure MPP payment). Join succeeds — resolveVerifiedMppWalletPrincipal(payment.payer) creates a wallet-kind principal and the tank spawns.
  2. POST /api/v1/games/{gameId}/commands with any command → 401 authentication_required ("Create a browser session or send Tact-Agent-Token.").

Cause: requireRequestPrincipal (apps/web/src/lib/server/identity.ts) only resolves tact_agent_* tokens and tact_session cookies. There is no wallet→token exchange, and /commands issues no MPP/SIWX challenge. So a wallet-DID principal has no way to authenticate outside of paid routes — the tank is permanently frozen.

The AgentCash origin guidance says "the verified payer wallet becomes the agent identity, so AgentCash can join without an API key" — true for joining, but the resulting player is a zombie. Game 913c235d-7ee1-4931-a3f8-4a51e48691e6 ("AgentCash Production Match") on prod now contains two such zombie tanks ("AgentCash Prod" and "Fable") in a match that can never progress.

Possible fixes: accept an MPP identity credential on /commands (mpp.dev "Identity" flow), or return a tact_agent_* token in the paid-join response for wallet principals, or document that paid joins must include a pre-provisioned Tact-Agent-Token header (the existingPrincipal ?? payer fallback in the join route already supports this — it's just undiscoverable).

2. www.tact.wtf 308 redirect breaks all MPP payments

https://www.tact.wtf/* returns 308https://tact.wtf/*. The MPP challenge/credential flow doesn't survive the redirect, so every paid call through the www host fails with mpp_payment_rejected. npx agentcash try https://www.tact.wtf (and the registered origin listing) advertises the www URL, so a fresh agent's first payment always fails until it discovers the apex domain by hand-inspecting the redirect.

Fix: either serve the API on both hosts or register/advertise only the apex origin.

3. Idempotent join replays still settle a new MPP charge

Replaying POST /join with the same commandId/idempotencyKey correctly returns "replayed": true and doesn't double-charge the app-level buy-in (prize pool unchanged) — but each replay settles a fresh MPP payment (distinct tx hashes per attempt: 0x7adeca5a…, 0x9c442a43…). withMppCharge wraps the handler, so payment settles before the idempotency check runs. Retrying agents get charged the entry price every retry.


Happy to provide tx hashes / request logs. Filed by Claude on behalf of @sragss.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions