Skip to content

covenant-invoica: capability-gated, audit-logged connector for Invoice + settlement#113

Open
mizuki0x wants to merge 1 commit into
mainfrom
feat/invoica
Open

covenant-invoica: capability-gated, audit-logged connector for Invoice + settlement#113
mizuki0x wants to merge 1 commit into
mainfrom
feat/invoica

Conversation

@mizuki0x

@mizuki0x mizuki0x commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Adds covenant-invoica, a read-only-first connector that exposes Invoica as native, capability-gated, audit-logged Covenant tools. Covenant scopes, brokers, and audits the call; Invoica keeps the money and compliance rail. Nothing here touches settlement custody.

What it adds

Four MCP tools, all off by default (COVENANT_INVOICA_ENABLED + an API key required):

  • invoica.invoice.create: create an invoice (amount in dollars, customerEmail, customerName, optional currency/chain/buyerCountryCode/companyId)
  • invoica.invoice.get: fetch by id
  • invoica.invoice.list: list with status/paging filters
  • invoica.settlement.check: settlement status for an invoice

The daemon brokers the API key: it lives in the client, injected as Authorization: Bearer, and never enters config, the operator token, or the audit log. Every call is capability-gated (tool.call.invoica.*) and lands on the audit chain through the daemon's generic tool path, so there is no bespoke dispatcher.

Contract targeted

Built against the backend openapi.json rather than the published @invoica/sdk, because the SDK is missing the /v1/mandates* (PACT) and /api/x402/* surface the live backend exposes. Env values are trimmed at the boundary, arguments are validated before any HTTP call, GET retries a cold-starting gateway and honors Retry-After, create is single-shot (a created invoice is not idempotent), and an empty or null success body surfaces as an error rather than a null invoice. 20 tests, clippy clean, fmt clean. Verified end to end through the real daemon and a covenant CLI against an OpenAPI-accurate mock, and separately by a cold-user pass that confirmed the key never appears in tool output.

Blocked on / open questions for @SkinGem

Live validation is not yet possible and the contract has some internal drift worth confirming:

  1. api.invoica.ai is returning 502 on every endpoint (health, /v1/invoices, /api/x402/*, /v1/mandates), held for 25s+ so it reads as a dead upstream, not a cold start. app.invoica.ai is 200. A working API and a devnet/test key would let us validate live.
  2. openapi.json (last updated May 19) and backend/src/routes/invoices.ts (June 12) disagree on the create-invoice body. The spec lists required customerEmail/customerName/amount with chain optional; the handler destructures invoiceNumber/amount/currency/customerEmail/customerName/companyId and never reads chain or buyer*. Which is canonical? The connector currently follows the spec.
  3. amount unit: confirmed dollars from the handler (parseFloat, no x100); the OpenAPI leaves it undescribed. Flagging so it stays that way.
  4. Settlement: there is no free per-invoice settlement route; canonical is the x402-paid /api/x402/settle (or settlement.* webhooks). invoica.settlement.check currently projects status from GET /v1/invoices/:id; the paid path is Phase 2.

Not in this PR (Phase 2+)

Provenance envelope per payment, live x402 + SAP escrow, tax (/api/x402/tax, paid), and the PACT-mandate to capability directional mapping.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant