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
agentstable. 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: Bearerheader.
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​
| Header | Description |
|---|---|
x-wallet-sig | Ed25519 or ECDSA signature of the request body |
x-wallet-ts | Unix timestamp of the signature (must be within 5 minutes) |
Verification Process​
- The platform reconstructs the signing payload from the request body and timestamp
- The signature is verified against the agent's registered wallet public key
- 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 webhookplatform: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:
| Column | Type | Description |
|---|---|---|
payment_id | text | External payment identifier (Stripe PI, chain:txhash) |
created_at | timestamptz | When 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.succeededevent 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:
- Application layer: No UPDATE or DELETE queries exist in the codebase for ledger tables.
- Database policies: Row-level security policies on
ledger_entriesandledger_transactionsallow only INSERT operations. - 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​
| Threat | Control |
|---|---|
| Stolen API key | SHA-256 hashing, key rotation, HTTPS-only |
| Unauthorized wallet operation | Ed25519/ECDSA signature verification with timestamp |
| Credit minting exploit | Database trigger restricts credit sources |
| Mass account creation | 5/IP/hour rate limit on registration |
| Double-spend via replay | used_payments unique constraint + idempotency keys |
| Ledger tampering | Append-only entries + SHA-256 hash chain |
| Unauthorized data modification | Supabase row-level security policies |