What We Receive

Updated

Asqav signs a record of each action your agent takes. To do that it needs a fingerprint of the action, not the action itself. When the SDK talks to the Asqav cloud (api.asqav.com), it hashes the action context on your machine and sends the hash plus a short list of labels. Your prompts, the messages your agent exchanges, tool arguments, file contents, model output and any secrets in the context stay with you.

This page lists every field that leaves your machine, what stays local, how long we keep what we receive, and three ways to check all of it yourself.

How it works

  1. Your code calls agent.sign("tool:read_file", context). The context can hold anything: the prompt, the file, the tool's arguments.
  2. The SDK turns the action type and context into canonical JSON and computes its SHA-256 hash locally. If you set an org_salt, the hash is HMAC-SHA-256 keyed by a salt only you hold.
  3. The SDK sends the hash, the byte length of the canonical context, and the labels in the table below. The context itself is not in the request.
  4. Asqav signs that record with ML-DSA-65, links it to the previous receipt and returns the signed receipt.

The SDK picks this hash-only mode automatically for any *.asqav.com address. The cloud enforces it too: a sign request that carries a raw context is refused with 400 MODE_NOT_ALLOWED unless your organisation has signed a separate agreement to store full payloads. Metadata keys outside the allowed list are dropped on arrival.

What is sent

CallFields sentNotes
POST /agents/{id}/sign and /sign-batchaction_type, hash, hash_algo, payload_size, session_id, nonce, metadatametadata holds only agent_id, session_id, action_type and, if you opt in, model_name, tool_name, parent_id. nonce is random.
Same call, when you pass a resultresult_digestA SHA-256 of the result, computed locally. The result is not sent.
Same call, receipt fieldsreceipt_type, policy_decision, action_ref, plus any optional field you pass by nameaction_ref is a digest. Optional fields (for example reason, risk_class, repo_ref, commit_sha) are sent only when your code sets them, and are stored as you wrote them.
POST /agents/create, GET /agents/{id}Agent name, algorithm, capabilitiesNames you choose for your agents.
POST /sessions/ and session updatesagent_id, session statusGroups receipts. No content.
Suspend, revoke, emergency haltThe reason string you passOnly when you call these.
POST /audit-pack/exportTime window, optional agent_idDownloads your receipts.
GET /.well-known/jwks.jsonNothingFetches public verification keys.
Every requestX-API-Key header, User-Agent with the SDK name and versionOur servers log the source IP and user agent for security, as the privacy policy describes.

The action_type label travels in clear text, so keep it a category such as tool:read_file or llm:call. Do not put a prompt, a query or a customer name in it. The same goes for agent names and any optional field you choose to set.

What stays on your machine

Self-hosted deployments run the signer on your own infrastructure, so nothing reaches Asqav at all.

How long we keep it

Check it yourself

1. Run our egress test. The public asqav-sdk repository has a test that plants a prompt, a file body and a secret-shaped key in the context, runs every signing path against the cloud address, captures each request on the wire and fails if any planted value appears. A second run in full-payload mode must leak, which proves the check works.

git clone https://github.com/jagmarques/asqav-sdk
cd asqav-sdk/python && pip install -e ".[dev]"
pytest tests/test_no_sensitive_egress.py
cd ../typescript && npm ci && npx vitest run tests/noSensitiveEgress.test.ts

2. Watch the real request. Point the SDK at a listener on your machine and read what arrives. Set ASQAV_MODE=hash-only, because a non-Asqav address would otherwise default to full payload.

nc -l 8080 &
ASQAV_API_KEY=sk_test_local ASQAV_MODE=hash-only ASQAV_API_URL=http://127.0.0.1:8080 python3 -c '
import asqav
asqav.init()
asqav.Agent("agt_1", "demo", "pk", "kid", "ml-dsa-65", [], 0.0).sign(
    "llm:call", {"prompt": "my secret prompt"})'

The body nc prints carries a hash and no prompt. The Python call then fails because nc sends no reply, which is expected.

3. Recompute the hash. The hash on a receipt is SHA-256 over the canonical form described in the fingerprint spec. Recompute it from the context you kept and compare. A match shows the receipt covers your action, without the action ever leaving your systems.

One limit to know

An unsalted hash of a predictable action can be guessed by someone who holds the hash and can try candidate values. For contexts drawn from a small set, set an org_salt at asqav.init(). Data Handling explains the trade-off.

References