@dcprotocol/server
Fastify-based REST API server providing HTTP access to your DCP vault. Perfect for local agents built with any language or framework.
Installation
npm install @dcprotocol/serverQuick Start
# Start server (default port 8420) npx @dcprotocol/server # Or with custom port npx @dcprotocol/server --port 3000 # Server runs at http://127.0.0.1:8420
Overview
The DCP server provides a REST API for agents to interact with the vault. It runs on localhost only (127.0.0.1) for security - never exposed to the public internet.
When to Use
- Building agents in Python, Go, Rust, or other non-JS languages
- Using frameworks that don't support MCP (Langchain, AutoGen, etc.)
- Need HTTP/REST instead of stdio or native SDK
- Want browser-based consent UI instead of terminal prompts
Architecture
┌─────────────────┐
│ Your Agent │ (Python/Go/Any Language)
│ localhost:3000 │
└────────┬────────┘
│ HTTP
▼
┌─────────────────────┐
│ DCP Server │ http://127.0.0.1:8420
│ @dcprotocol/server │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ @dcprotocol/core │ SQLite vault @ ~/.dcp
└─────────────────────┘API Endpoints
GET /health
Health check endpoint.
curl http://127.0.0.1:8420/health
Response:
{
"status": "ok",
"vault_unlocked": true
}GET /scopes
List all available wallets and data in vault (capability discovery).
curl http://127.0.0.1:8420/scopes
Response:
{
"scopes": [
{
"scope": "wallet:solana",
"type": "WALLET_KEY",
"sensitivity": "critical",
"chain": "solana",
"public_address": "5YzW8d...AbC123"
},
{
"scope": "identity.email",
"type": "DATA",
"sensitivity": "sensitive",
"chain": null,
"public_address": null
}
]
}
# Use this endpoint to discover what the agent can accessGET /address/:chain
Get wallet address for a blockchain.
curl http://127.0.0.1:8420/address/solana
Response:
{
"address": "5YzW8d...AbC123",
"chain": "solana"
}
# Supported chains: solana, ethereum, basePOST /v1/vault/unlock
Unlock vault with passphrase.
curl -X POST http://127.0.0.1:8420/v1/vault/unlock \
-H "Content-Type: application/json" \
-d '{"password": "your-passphrase"}'
Response:
{
"success": true,
"session_expires_at": "2026-03-20T11:30:00Z"
}POST /v1/vault/sign
Sign a blockchain transaction.
curl -X POST http://127.0.0.1:8420/v1/vault/sign \
-H "Content-Type: application/json" \
-d '{
"chain": "solana",
"agent_name": "my-trading-bot",
"amount": 1.5,
"currency": "SOL",
"unsigned_tx": "base64-encoded-transaction"
}'
Response:
{
"signed_tx": "base64-encoded-signed-transaction",
"signature": "transaction-signature-hex"
}Request Fields
| Field | Type | Description |
|---|---|---|
chain | string | solana | ethereum | base |
agent_name | string | Name of agent making request |
amount | number | Transaction amount |
currency | string | SOL | ETH | BASE_ETH | USDC |
unsigned_tx | string | Base64-encoded unsigned transaction |
POST /v1/vault/read
Read encrypted data from vault.
curl -X POST http://127.0.0.1:8420/v1/vault/read \
-H "Content-Type: application/json" \
-d '{
"scope": "identity.email",
"agent_name": "my-agent"
}'
Response:
{
"value": "john@example.com",
"scope": "identity.email",
"sensitivity": "sensitive"
}GET /budget/check
Check if transaction is within budget limits.
curl "http://127.0.0.1:8420/budget/check?chain=solana&amount=1.5¤cy=SOL"
Response:
{
"allowed": true,
"requires_approval": false,
"remaining_daily": 18.5,
"remaining_tx": 3.5
}GET /v1/vault/activity
Get audit log.
curl http://127.0.0.1:8420/v1/vault/activity?limit=10
Response:
{
"events": [
{
"timestamp": "2026-03-20T10:30:00Z",
"agent": "TradingBot",
"action": "SIGN",
"chain": "solana",
"amount": 1.5,
"currency": "SOL",
"status": "APPROVED"
}
]
}POST /v1/vault/consent/approve
Approve pending consent request (from browser UI).
curl -X POST http://127.0.0.1:8420/v1/vault/consent/approve \
-H "Content-Type: application/json" \
-d '{
"consent_id": "consent-uuid-123"
}'
Response:
{
"success": true
}POST /v1/vault/consent/deny
Deny pending consent request.
curl -X POST http://127.0.0.1:8420/v1/vault/consent/deny \
-H "Content-Type: application/json" \
-d '{
"consent_id": "consent-uuid-123"
}'
Response:
{
"success": true
}Consent UI
When an agent requests a sensitive operation (signing, reading data), the server automatically opens a browser window at http://127.0.0.1:8420/consentshowing the request details and approve/deny buttons.
Consent Flow
1. Agent calls POST /v1/vault/sign 2. Server creates consent request 3. Browser window opens showing: - Agent name - Operation (sign transaction) - Amount and currency - Transaction details - Approve/Deny buttons 4. User clicks Approve 5. Server processes request 6. Agent receives signed transaction
Trusted Agents
Agents marked as "trusted" skip the consent UI for transactions below the auto-approve threshold.
# Trust an agent via CLI dcp agents trust TradingBot # Now TradingBot can auto-sign transactions under the threshold # (default: 2 SOL, 0.1 ETH, 100 USDC)
Integration Examples
Python
import requests
BASE_URL = "http://127.0.0.1:8420"
# Get Solana address
response = requests.get(f"{BASE_URL}/address/solana")
address = response.json()["address"]
print(f"Solana address: {address}")
# Sign transaction
response = requests.post(f"{BASE_URL}/v1/vault/sign", json={
"chain": "solana",
"agent_name": "python-trader",
"amount": 1.5,
"currency": "SOL",
"unsigned_tx": unsigned_tx_base64
})
if response.status_code == 200:
result = response.json()
signed_tx = result["signed_tx"]
print(f"Signed: {signed_tx}")
else:
print(f"Error: {response.json()}")Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
const baseURL = "http://127.0.0.1:8420"
func getAddress(chain string) (string, error) {
resp, err := http.Get(fmt.Sprintf("%s/address/%s", baseURL, chain))
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
Address string `json:"address"`
}
json.NewDecoder(resp.Body).Decode(&result)
return result.Address, nil
}
func signTransaction(chain string, amount float64, unsignedTx string) (string, error) {
payload := map[string]interface{}{
"chain": chain,
"agent_name": "go-trader",
"amount": amount,
"currency": "SOL",
"unsigned_tx": unsignedTx,
}
body, _ := json.Marshal(payload)
resp, err := http.Post(
baseURL+"/v1/vault/sign",
"application/json",
bytes.NewBuffer(body),
)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
SignedTx string `json:"signed_tx"`
}
json.NewDecoder(resp.Body).Decode(&result)
return result.SignedTx, nil
}cURL Scripts
#!/bin/bash
# save as dcp-sign.sh
AGENT_NAME="bash-trader"
CHAIN="solana"
AMOUNT=1.5
TX="base64-encoded-transaction"
curl -X POST http://127.0.0.1:8420/v1/vault/sign \
-H "Content-Type: application/json" \
-d "{
\"chain\": \"$CHAIN\",
\"agent_name\": \"$AGENT_NAME\",
\"amount\": $AMOUNT,
\"currency\": \"SOL\",
\"unsigned_tx\": \"$TX\"
}" | jq -r '.signed_tx'Server Configuration
Command-Line Options
| Option | Default | Description |
|---|---|---|
--port | 8420 | HTTP port to listen on |
--host | 127.0.0.1 | Host to bind to (localhost only) |
--vault-dir | ~/.dcp | Vault directory |
--no-browser | false | Disable auto-opening consent UI |
Environment Variables
| Variable | Default | Description |
|---|---|---|
VAULT_DIR | ~/.dcp | Vault storage directory |
DCP_SERVER_PORT | 8420 | HTTP port |
DCP_NO_BROWSER | false | Disable consent browser UI |
Security Considerations
- Localhost only: Server binds to 127.0.0.1 and cannot be accessed remotely
- No authentication: Assumes localhost is trusted (single-user machine)
- CORS restricted: Only allows requests from localhost origins
- Session-based: Vault unlocks expire after inactivity
- Consent required: All sensitive operations require user approval (unless trusted)
⚠️ Never expose to internet: This server is designed for localhost only. Use @dcprotocol/proxy for remote access via encrypted relay.
Production Deployment
systemd Service
Run server as systemd service on Linux:
# /etc/systemd/system/dcp-server.service [Unit] Description=DCP Vault Server After=network.target [Service] Type=simple User=youruser ExecStart=/usr/local/bin/npx @dcprotocol/server Restart=on-failure Environment="VAULT_DIR=/home/youruser/.dcp" [Install] WantedBy=multi-user.target # Enable and start sudo systemctl enable dcp-server sudo systemctl start dcp-server
Docker
# Not recommended - vault should stay on host machine # If you must use Docker, mount vault directory: docker run -d \ --name dcp-server \ -p 127.0.0.1:8420:8420 \ -v ~/.dcp:/root/.dcp \ node:18 \ npx @dcprotocol/server
Troubleshooting
Port Already in Use
# Error: Port 8420 already in use # Use different port npx @dcprotocol/server --port 3000 # Or kill existing process lsof -ti:8420 | xargs kill
Vault Locked
# Error: Vault is locked
# Unlock via API
curl -X POST http://127.0.0.1:8420/v1/vault/unlock \
-H "Content-Type: application/json" \
-d '{"password": "your-passphrase"}'Consent UI Not Opening
# If browser doesn't open automatically # Manually navigate to: http://127.0.0.1:8420/consent # Or disable browser UI and handle via API npx @dcprotocol/server --no-browser
See Also
- @dcprotocol/core - Underlying vault engine
- @dcprotocol/client - TypeScript SDK alternative
- @dcprotocol/mcp - MCP server for Claude Desktop
- @dcprotocol/proxy - VPS proxy for remote access