Point VEIL Scan at a fee payer your app uses in development. The debugger lists plaintext memos, noisy logs, and raw instruction data from the last 100 public signatures. Nothing here requires a wallet adapter.
Role of the debugger
A cited leak list, not an auditor
The debugger pane on a scan report is the builder-facing slice of the same public read. It highlights payload-shaped data: memos, logs that look like emails or URLs, and unparsed instruction bytes. Account lists, amounts, and program ids remain public even when this pane is empty. Absence of a memo is not a shielded transfer.
What the debugger is for
Seeing which memos, msg!() lines, and instruction bytes a fee payer already published
Copying the same structured report the HTML page shows, for a later CI check
Teaching a team which fields are public before they encode a user id into ix data
What the debugger is not
A program security audit, fuzzer, or privilege-escalation scanner
A deanonymization graph that merges CEX KYC or social accounts
A shielded-transaction builder — hygiene is not a zk proof
Debugger workflow
How to read a leak
01
Point the scanner at a fee payer your app uses on mainnet or a busy public example.
02
Open Debugger. Each leak cites a signature, an excerpt, and a concrete recommendation.
03
Copy JSON if you want the structured report in CI later. POST /api/scan returns the same shape.
04
Fix the client or program, then scan again. The ledger will not forget the old signatures.
Leak patterns
What usually shows up on-chain
01
Memos
If the client writes invoice numbers, emails, or user ids into the Memo program, every explorer shows them.
Keep correlation ids off-chain, or commit to a hash. Never put PII in MemoSq4gq… or Memo1Uhk….
02
Program logs
msg!() is not a private logger. RPC nodes return log lines with the transaction metadata.
Log instruction names and error codes only. Strip emails, URLs, order ids, and customer keys before you print.
03
Instruction data
Unparsed bytes are still public. Encoding a session token or customer key here publishes it.
Pass opaque commitments or encrypted payloads. Do not serialize application JSON into ix data.
04
Account lists
Extra remaining accounts can link a user wallet to a program PDA they did not need to expose.
Minimize remaining accounts. Prefer lookup tables only for accounts the program must actually touch.
05
Reuse
One hot wallet across mint, swap, and bridge is a join key. Split roles if the product needs separation.
Fee payer ≠ treasury ≠ user escrow. Document which key is allowed to appear in which flow.
06
Failed transactions
A failed swap still shows the program, the accounts, and often the attempted amount. Failure is not erasure.
Simulate before send, and treat simulation logs as public too if you persist them.
07
Associated token accounts
ATAs derive from a wallet. Listing a user’s token account next to a program PDA still points at the wallet.
Do not pass a user’s main wallet as a remaining account unless the program must debit it. Prefer program-owned escrow.
08
Lookup tables
Address lookup tables shrink transaction size. They do not hide accounts. Loaded writable and readonly keys still appear in metadata.
Treat every ALT member as public. Do not stash ‘private’ config accounts in a table and assume explorers will miss them.
POST /api/scan
Same report, as JSON
Send a JSON body with a Solana address or demo. Live scans return source: "live" plus signature evidence. Invalid addresses return HTTP 400. RPC failures return HTTP 502 with a readable error. Use demo when you only need the shape.
curl -sS -X POST http://localhost:3000/api/scan \
-H 'content-type: application/json' \
-d '{"address":"5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9"}'
# Fixture shape, no RPC:
curl -sS -X POST http://localhost:3000/api/scan \
-H 'content-type: application/json' \
-d '{"address":"demo"}'
No auth header. No wallet proof. Change the host for your deploy. Live scans can take several seconds while 100 transactions are fetched.
Status codes
200 — Report JSON. source is live or sample (demo).
400 — Missing body, empty address, or not valid base58. ScanInputError.
502 — Public RPC failed or rate-limited. Set SOLANA_RPC_URL.
Useful fields: address, source, rpcLabel, scannedSignatures, fetchedTransactions, exposureScore, findings[], transfers[], apps[], debugLeaks[], warnings[]. Each finding and leak should carry a signature you can open in an explorer. Pipeline details: How it works.
Hosting
RPC quota and port binding
Live scans are only as good as the RPC you give them. Set SOLANA_RPC_URL to a dedicated mainnet endpoint in production. Public fallbacks exist so local demos work; they rate-limit. Web processes should listen on 0.0.0.0:$PORT. This app does not use the local disk as a database — typical PaaS filesystems are ephemeral anyway. There is no auth header and no token.
Example addresses
Exchange hot wallet
A labeled Binance-related mainnet wallet with dense public transfers. Use it to see a busy trail — not a private person.