# Security, privacy and stability

> For teams evaluating hashspan: what it can and can't do with keys, what it records by default, how to report a vulnerability, and what 1.0 keeps stable.

Source: https://hashspan.dev/docs/security

Evaluating hashspan for a team? This page collects the answers in one place, with a link to the source of each.

## Can it touch my keys or move funds?

No. hashspan never signs or broadcasts a transaction, and it needs no private key. Its own RPC calls are read-only: it reads receipts, and replays a reverted transaction as a call to decode the reason ([requests it adds](https://hashspan.dev/docs/architecture)).

It runs inside your app, and its instrumentation never throws into or changes the result of your call.

## What does it record by default?

| Data | Default | How to change it |
|---|---|---|
| Addresses (sender, recipient, payer, paymaster…) | Recorded, in lower case | `address: 'hashed'` or `'off'`, or your own `hash` function |
| Transaction hash | Recorded | Keep it: it is how spans are matched to the chain |
| Amounts (value, payment amount, fees) | Recorded | Remove from spans with a `redact` hook; the fee histogram keeps fees, so drop it with a metrics View |
| Agent identity (`gen_ai.agent.*`) | Recorded when set, also from incoming Baggage | `agentFromBaggage: false`; don't put personal names in `agent` |
| Calldata arguments | Not recorded | Opt in with `recordFunctionArguments` |
| Error messages | Not recorded, only the error type | `errorMessages: 'sanitized'` or `'raw'` to opt in |
| Paid URLs (x402, MPP) | Only the origin | `paymentResource: 'path'` or `'off'` |
| JSON-RPC spans (RPC host and method) | Not recorded | Opt in with `traceTransport()` (viem) |
| Metrics | Durations and fees, by chain, operation and outcome; never an address, hash or agent identity | Drop a histogram with a metrics View |

The full list is in [what is recorded](https://hashspan.dev/docs/adapters/core#what-is-recorded). The `redact` hook runs on every span attribute set, but not on metrics or JSON-RPC spans.

### For strict privacy reviews

- **Hashed mode is not secret.** It is an unkeyed SHA-256, and addresses are public, so a hash can be matched by hashing candidates. Use `'off'`, or a keyed hash such as an HMAC with a secret you keep.
- **Retention and location are your backend's.** hashspan keeps nothing; spans live where your OpenTelemetry backend stores them, for as long as it keeps them.

A strict setup:

```ts
import { createHmac } from 'node:crypto';
import { withHashspan } from '@hashspan/viem';

declare const key: string; // a secret from your secret store, not from code

// Strict: no plain addresses, no error text, no identity from incoming Baggage, no paid URLs.
export const strict = withHashspan({
  address: { mode: 'hashed', hash: (address) => `hmac:${createHmac('sha256', key).update(address).digest('hex').slice(0, 32)}` },
  errorMessages: 'off',
  agentFromBaggage: false,
  paymentResource: 'off',
});
```

The library sends no telemetry to the project. Spans go only to the OpenTelemetry backend you set up. Hashing or dropping addresses limits what your backend stores, but it is not anonymity: a transaction hash leads to its parties on chain. The [privacy section](https://hashspan.dev/docs/reference/semantic-conventions#privacy) lists every attribute each mode covers. To drop them in a Collector instead, see the [Collector recipe](https://hashspan.dev/docs/recipes/collector).

## How do I report a vulnerability?

Privately, through GitHub's [private vulnerability reporting](https://github.com/selimaytac/hashspan/security/advisories/new), not in a public issue. The [security policy](https://github.com/selimaytac/hashspan/blob/main/SECURITY.md) says to expect an acknowledgement within 5 business days. A confirmed report then gets a fix, a disclosure date agreed with you, and a published advisory.

Security fixes go to the latest minor of each package: 1.x for `core`, `viem`, `cdp` and `x402`, and the latest 0.x for `mpp`. The policy also lists what is in and out of scope, such as an address recorded in a mode that should keep it out.

## What does 1.0 keep stable?

- **The public API.** Since 1.0, a breaking change to the TypeScript API needs a major release. Each package's API report lists what is public, and CI fails when it is stale ([ADR 0027](https://github.com/selimaytac/hashspan/blob/main/docs/adr/0027-what-1-0-freezes.md)).
- **Not yet the span and attribute names.** They stay development. A renamed attribute or metric is recorded under both names for at least one minor release, announced in the changelog, with a schema version bump. A span name changes only in a minor release, announced the same way ([change policy](https://hashspan.dev/docs/reference/semantic-conventions)).

So your code keeps compiling across 1.x. Check the [changelog](https://hashspan.dev/changelog) before a minor upgrade if dashboards depend on attribute names.

## How is it released?

From GitHub Actions, with npm provenance: npm shows a provenance badge for each version, linking it to its build. Each GitHub release carries a CycloneDX SBOM of the lockfile.

## Who maintains it?

hashspan is open source under the Apache-2.0 license, maintained in the open on [GitHub](https://github.com/selimaytac/hashspan). Significant design decisions are recorded as [ADRs](https://github.com/selimaytac/hashspan/tree/main/docs/adr). The license lets anyone use, change and fork it.
