Skip to main content

Security

This page documents the security mechanisms protecting the Paygrid platform, covering authentication, authorization, data integrity, and abuse prevention.

API Key Authentication​

Every agent receives an API key at registration. Keys are handled securely:

  • Hashing: API keys are hashed with SHA-256 before storage. The plaintext key is returned once at registration and never stored.
  • Storage: Hashed keys live in the Supabase agents table. Authentication compares the SHA-256 hash of the provided key against the stored hash.
  • Transport: All API requests must use HTTPS. Keys are passed in the Authorization: Bearer header.
Authorization: Bearer ak_live_xxxxxxxxxxxxxxxx

If a key is compromised, the agent can rotate it via the API. The old hash is invalidated immediately.

Wallet Signature Verification​

For operations involving on-chain assets (deposits, withdrawals to wallet), the platform verifies wallet ownership via cryptographic signatures:

Required Headers​

HeaderDescription
x-wallet-sigEd25519 or ECDSA signature of the request body
x-wallet-tsUnix timestamp of the signature (must be within 5 minutes)

Verification Process​

  1. The platform reconstructs the signing payload from the request body and timestamp
  2. The signature is verified against the agent's registered wallet public key
  3. The timestamp is checked to be within a 5-minute window to prevent replay

If any check fails, the request is rejected with a 401 status.

Credit Source Validation​

A Supabase database trigger enforces that credits can only be created from approved sources. This is the most critical security control in the platform.

Approved Sources​

  • platform:stripe — fiat deposits confirmed via Stripe webhook
  • platform:circle — USDC deposits confirmed via Circle API
  • On-chain deposit monitoring — confirmed blockchain transactions

How It Works​

The trigger fires on every INSERT to the ledger_entries table. If an entry would credit an agent:* account and the corresponding debit does not come from an approved source account, the INSERT is rejected.

-- Simplified trigger logic
CREATE OR REPLACE FUNCTION validate_credit_source()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.account LIKE 'agent:%' AND NEW.amount > 0 THEN
-- Verify the transaction has a matching debit from an approved source
IF NOT EXISTS (
SELECT 1 FROM ledger_entries
WHERE transaction_id = NEW.transaction_id
AND amount < 0
AND account IN ('platform:stripe', 'platform:circle', ...)
) THEN
RAISE EXCEPTION 'Unauthorized credit source';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

This prevents any application bug or exploit from minting credits without a corresponding real-money deposit.

Rate Limiting​

Registration​

Agent registration is rate-limited to 5 requests per IP address per hour. This prevents mass account creation for abuse.

API Requests​

Authenticated endpoints are rate-limited per API key. Limits vary by endpoint but are designed to allow normal agent operation while preventing abuse.

Implementation​

Rate limits are enforced at the application layer using sliding window counters backed by the database.

Replay Protection​

The used_payments table prevents the same external payment from being credited twice:

ColumnTypeDescription
payment_idtextExternal payment identifier (Stripe PI, chain:txhash)
created_attimestamptzWhen the payment was processed

Before crediting an agent for any external payment, the platform checks used_payments. If the payment ID already exists, the credit is skipped. This handles:

  • Stripe webhook retries — Stripe may send the same payment_intent.succeeded event multiple times
  • On-chain transaction reprocessing — deposit monitor restarts may re-scan recent blocks
  • Manual corrections — operators cannot accidentally double-credit

The payment_id column has a unique constraint, so even concurrent attempts to process the same payment will fail at the database level.

Append-Only Ledger​

Ledger entries are never updated or deleted. This is enforced at multiple levels:

  1. Application layer: No UPDATE or DELETE queries exist in the codebase for ledger tables.
  2. Database policies: Row-level security policies on ledger_entries and ledger_transactions allow only INSERT operations.
  3. Integrity hash chain: Any modification to historical entries would break the SHA-256 hash chain, which is verified periodically.

Corrections are made by adding reversing entries — a new transaction that debits what was previously credited and vice versa. The original entries remain permanently in the ledger.

Summary of Controls​

ThreatControl
Stolen API keySHA-256 hashing, key rotation, HTTPS-only
Unauthorized wallet operationEd25519/ECDSA signature verification with timestamp
Credit minting exploitDatabase trigger restricts credit sources
Mass account creation5/IP/hour rate limit on registration
Double-spend via replayused_payments unique constraint + idempotency keys
Ledger tamperingAppend-only entries + SHA-256 hash chain
Unauthorized data modificationSupabase row-level security policies