Skip to Content
Welcome to our new docs! 🎉

Private blockspace quickstart

Private blockspace encrypts your blob data before it’s posted to Celestia using a lightweight proxy.
In this quickstart, you’ll use hosted Celestia nodes (like QuickNode) instead of running your own.

Get your node credentials

To use Private Blockspace, you’ll need access to a hosted Celestia Data Availability (DA) + Consensus Node. The easiest option is QuickNode’s Celestia service , which provides ready-to-use mainnet and Mocha testnet endpoints. They offer a free 30-day trial, which is enough for this quickstart.

  1. Visit the QuickNode Celestia dashboard  and log in (or create a free account).
  2. Click Create Endpoint, then select:
    • Chain: Celestia
    • Network: Mainnet or Mocha
  3. Copy the generated endpoint URL (it looks like this): https://celestia-mocha.quiknode.pro/<your-token>/
  4. Copy the token from your endpoint URL (this is your write / read token).
  5. Save both the URL and token — you’ll add them to your .env file next.

Create your .env

Download the repo’s example.env and save it as .env.
Edit only the following items.

KeyWhat to do
PDA_DB_PATHAbsolute path for local proxy data (e.g. /Users/you/dev/pda-db)
ENCRYPTION_KEY32-byte key — see command below
CELESTIA_NODE_RPCYour QuickNode HTTPS endpoint (includes your token)
CELESTIA_CORE_GRPCSame host + :9090 (no token in path)
CELESTIA_NODE_WRITE_TOKENYour QuickNode token
CELESTIA_SIGNING_KEY32-byte private key for your signer (funded on Mocha)
TLS_CERTS_PATH / TLS_KEY_PATHPath to your local TLS cert + key (see next step)
CELESTIA_NETWORK etc.Keep defaults; ensure all paths are absolute

Generate an ENCRYPTION_KEY (ChaCha20, 32 bytes)

This is a random 256-bit symmetric key, not a wallet key.

openssl rand -hex 32 # or python3 -c "import secrets; print(secrets.token_hex(32))"

Paste the value (no quotes) as your ENCRYPTION_KEY.

Generate TLS certificates

The proxy requires TLS certificates (even for local testing).

mkdir -p tls openssl req -x509 -nodes -newkey rsa:2048 -keyout tls/tls.key -out tls/tls.crt -days 365 -subj "/CN=localhost"

Then mount that folder when running Docker and set:

TLS_CERTS_PATH=/app/tls/tls.crt TLS_KEY_PATH=/app/tls/tls.key

Without valid certs, the proxy will exit with
TLS_CERTS_PATH required: NotPresent.

Fund your signer

Your CELESTIA_SIGNING_KEY corresponds to a Mocha testnet address.
That address must exist and hold some test TIA.

Get funds from the Celestia Mocha faucet .

You can verify your signer address in the proxy logs it prints the address when starting.

Run the proxy container

Apple Silicon (M1/M2/M3)

The published image is amd64 only — run it under emulation:

docker pull --platform=linux/amd64 ghcr.io/celestiaorg/pda-proxy:latest mkdir -p "$PDA_DB_PATH" docker run --name pda-proxy --rm -it --platform=linux/amd64 --env-file ./.env -v "$PDA_DB_PATH:$PDA_DB_PATH" -v "./tls:/app/tls" -p 26657:26657 ghcr.io/celestiaorg/pda-proxy:latest

Intel/AMD Linux or other amd64 hosts

docker pull ghcr.io/celestiaorg/pda-proxy:latest mkdir -p "$PDA_DB_PATH" docker run --name pda-proxy --rm -it --env-file ./.env -v "$PDA_DB_PATH:$PDA_DB_PATH" -v "./tls:/app/tls" -p 26657:26657 ghcr.io/celestiaorg/pda-proxy:latest

Verify it’s running

Check logs:

docker logs -f pda-proxy

Or test status:

curl -k -sS -X POST -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_TOKEN" --data '{"jsonrpc":"2.0","id":1,"method":"status","params":[]}' https://127.0.0.1:26657 | jq .

On Apple Silicon, keep --platform=linux/amd64 until a native arm64 image exists.

Send a blob

Try a small encrypted payload:

curl -k -sS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -X POST --data '{ "id": 1, "jsonrpc": "2.0", "method": "blob.Submit", "params": [[ { "namespace": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJ/xGlNMdE=", "data": "SSBMb3ZlIEJpZyBCbG9icw==", "share_version": 0, "commitment": "aHlbp+J9yub6hw/uhK6dP8hBLR2mFy78XNRRdLf2794=", "index": -1 } ], {}] }' https://127.0.0.1:26657 | jq .

The proxy will:

  • Encrypt your blob
  • Submit to Celestia
  • Return a height and tx hash

If the response includes "result": { "height": "…" }, you’re set. Jump to Verify your blob with that height.

If you see "[pda-proxy] Verifiable encryption processing...", the proxy queued the job. Two quick options:

Option A — resend once

Sometimes it finishes fast and you’ll get the height on the next call.

Option B — find the height

Either tail logs:

docker logs -f pda-proxy | egrep -i 'submit|height|commit|error'

Or poll a small window around the tip for your namespace:

LATEST=$( curl -k -sS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -X POST --data '{"jsonrpc":"2.0","id":1,"method":"status","params":[]}' \ https://127.0.0.1:26657 | jq -r '.result.sync_info.latest_block_height' ) for h in $(seq $((LATEST-2)) $((LATEST+2))); do RES=$( curl -k -sS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -X POST --data "{ \"id\":1, \"jsonrpc\":\"2.0\", \"method\":\"blob.GetAll\", \"params\":[ $h, [ \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJ/xGlNMdE=\" ] ] }" \ https://127.0.0.1:26657 ) if echo "$RES" | jq -e '.result | length > 0' >/dev/null; then echo "Height $h" echo "$RES" | jq . fi done

Copy the commitment you see there and use it with blob.Get.

Verify your blob

Now, fetch the same blob through your proxy using the following command with HEIGHT replaced with your actual height:

curl -k -sS -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_TOKEN" -X POST --data "{ \"id\":1, \"jsonrpc\":\"2.0\", \"method\":\"blob.GetAll\", \"params\":[HEIGHT,[\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJ/xGlNMdE=\"]] }" https://127.0.0.1:26657 | jq .

You’ll see a commitment and your decrypted data:

{ "commitment": "…base64…", "data": "SSBMb3ZlIEJpZyBCbG9icw==", "namespace": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJ/xGlNMdE=" }

Decode base64 to plaintext

When you retrieve a blob, the data field is base64. To see the plaintext:

macOS/Linux

echo "SSBMb3ZlIEJpZyBCbG9icw==" | base64 -d

Python

python3 - <<'PY' import base64 print(base64.b64decode("SSBMb3ZlIEJpZyBCbG9icw==").decode()) PY

Node.js

node -e 'console.log(Buffer.from("SSBMb3ZlIEJpZyBCbG9icw==","base64").toString())'

Troubleshooting

Error messageCauseFix
TLS_CERTS_PATH requiredMissing or commented-out cert varsGenerate certs (see above).
account not foundUnfunded signerUse Mocha faucet .
blob: not foundWrong commitmentRun blob.GetAll to find the real one.
grpc-status header missingInvalid gRPC URLMust be https://<host>:9090, no token.

✅ You’re done

You’re running private blockspace end-to-end using hosted infrastructure.

✅ No local Celestia node
✅ Fully containerized
✅ Encrypted data posted to Celestia

Feel stuck? Go to our Discord!

Last updated on