Skip to main content

Security middleware for x402 agentic payments — PII redaction, spending policy, and replay detection before blockchain commit

Project description

presidio-hardened-x402

PyPI version Python GitHub release License: MIT CI OpenSSF Scorecard OpenSSF Best Practices

v0.10.0 — treasury binding: x402 payment decisions reconcile into an audit-grade close via a signed off-chain settlement-ref@1 join record, a fail-closed bundle export/verify CLI, client-side settlement-receipt capture, and cross-language canonicalization conformance vectors.

Security middleware for the x402 payment protocol.

Intercepts x402 payment requests before transmission to servers and facilitators to enforce:

  • PII redaction — Presidio-based detection and redaction of personal data (emails, names, SSNs, credit cards, etc.) from payment metadata fields before they are sent to the payment server or facilitator API
  • Spending policy — per-agent, per-endpoint, and per-time-window budget limits that block or throttle payments before execution
  • Replay detection — HMAC-SHA256 fingerprinting of canonical payment fields to prevent duplicate and replayed payments; case-canonicalised on pay_to and currency (v0.4.0)
  • Audit logging — HMAC-chained JSON-L audit trail for every payment attempt (including blocked ones)
  • Multi-party authorization — n-of-m approval requirement for high-value payments, via webhook or HMAC-SHA256 cryptographic countersignature modes (v0.3.0)
  • Prometheus metrics — structured telemetry for every security control activation (v0.3.0)
  • Remote screening — opt-in remote_screening=True mode offloads PII analysis to the hosted screen.presidio-group.eu service via ScreeningClient; avoids the in-process spaCy dependency (v0.4.0)
  • Per-origin wallet allowlist — per-origin pay_to allowlist blocks payments to attacker-controlled addresses when a DNS-poisoned 402 response substitutes the recipient (chain-06 mitigation) (v0.4.0)
  • Rail-agnostic core + binding layer — the screening pipeline (ScreeningPipeline) is payment-protocol-independent; everything x402-specific lives in bindings/x402. Embed on a different rail by implementing PaymentProtocolBinding — the security guarantees travel with the core (v0.5.0)
  • OEM embed kitsemver/stability guarantees, partner-runnable conformance suite (python -m presidio_x402.conformance, 7 end-to-end checks, no network), and integration quickstarts for Coinbase CDP, LangChain, and CrewAI (v0.5.0)
  • Enterprise hardening — per-tenant replay namespaces, remote audit sinks, signed evidence verification, OpenTelemetry spans, hot-reloadable policy, release provenance, and latency-SLO CI gates (v0.6.0)

Part of the presidio-hardened-* toolkit family.


Installation

pip install presidio-hardened-x402

For full NLP-based PII detection (PERSON, ORG, location, etc.):

pip install "presidio-hardened-x402[nlp]"
python -m spacy download en_core_web_sm

For production replay guard with cross-process deduplication:

pip install "presidio-hardened-x402[redis]"

For Prometheus metrics export:

pip install "presidio-hardened-x402[prometheus]"

For policy-as-code schema validation:

pip install "presidio-hardened-x402[schema]"

Quick Start

Before (bare x402 — no security controls)

import httpx

async def pay_and_fetch(url: str, signer) -> httpx.Response:
    async with httpx.AsyncClient() as client:
        resp = await client.get(url)
        if resp.status_code == 402:
            payment_details = parse_402_header(resp.headers)
            # No PII check. No policy check. No replay check.
            payment_token = await signer.sign(payment_details)
            resp = await client.get(url, headers={"X-PAYMENT": payment_token})
        return resp

After (presidio-hardened-x402)

from presidio_x402 import HardenedX402Client

async def my_signer(details):
    # Your existing signing logic (eth-account, solders, CDP SDK, etc.)
    ...

client = HardenedX402Client(
    payment_signer=my_signer,
    policy={
        "max_per_call_usd": 0.10,
        "daily_limit_usd": 5.0,
        "per_endpoint": {"https://api.example.com": 1.0},
    },
    pii_action="redact",          # redact | block | warn
    pii_entities=["EMAIL_ADDRESS", "PERSON", "US_SSN", "CREDIT_CARD"],
    replay_ttl=300,               # seconds
)

response = await client.get("https://api.example.com/resource")

What happens transparently:

  1. Client sends GET /resource → server returns 402 with payment details
  2. PIIFilter scans resource URL, description, and reason fields; redacts any PII
  3. PolicyEngine checks: amount ≤ per-call limit? daily spend within budget? endpoint limit OK?
  4. ReplayGuard checks: have we paid this exact request within the TTL window?
  5. AuditLog records the attempt (pass or block) as a tamper-evident JSON-L entry
  6. If all checks pass, signer is called → payment header sent → resource returned

Configuration

Policy

from presidio_x402 import HardenedX402Client, PolicyConfig

policy = PolicyConfig(
    max_per_call_usd=0.05,         # block if maxAmountRequired > this
    daily_limit_usd=2.00,          # block if 24h aggregate spend would exceed this
    per_endpoint={
        "https://premium-api.io": 0.50,  # per-endpoint daily limit
    },
    window_seconds=86400,          # time window for aggregate limits (default: 24h)
    agent_id="my-agent-v1",        # tag audit events with an agent identifier
)

client = HardenedX402Client(payment_signer=signer, policy=policy)

PII Filter

# regex mode (default, zero-setup): catches structured PII
client = HardenedX402Client(
    payment_signer=signer,
    pii_mode="regex",
    pii_entities=["EMAIL_ADDRESS", "PHONE_NUMBER", "US_SSN", "CREDIT_CARD"],
    pii_action="redact",
)

# nlp mode: full spaCy NER (requires: pip install presidio-hardened-x402[nlp])
client = HardenedX402Client(
    payment_signer=signer,
    pii_mode="nlp",
    pii_entities=["EMAIL_ADDRESS", "PERSON", "LOCATION", "US_SSN"],
    pii_action="block",     # raise PIIBlockedError instead of redacting
)

Replay Guard

# In-memory (default, single-process)
client = HardenedX402Client(payment_signer=signer, replay_ttl=300)

# Redis-backed (production, cross-process)
client = HardenedX402Client(
    payment_signer=signer,
    replay_ttl=300,
    redis_url="redis://localhost:6379/0",
)

Audit Log

import sys
from presidio_x402 import HardenedX402Client
from presidio_x402.audit_log import StreamAuditWriter

client = HardenedX402Client(
    payment_signer=signer,
    audit_writer=StreamAuditWriter(sys.stdout),   # write JSON-L to stdout
)

Multi-Party Authorization (v0.3.0)

For high-value payments that require human or system oversight before execution:

from presidio_x402 import HardenedX402Client
from presidio_x402.mpa import MPAConfig, MPAApproverConfig, MPAEngine

mpa = MPAEngine(MPAConfig(
    threshold=2,               # require 2 of 3 approvals
    min_amount_usd=1.00,       # only for payments ≥ $1.00
    timeout_seconds=30,
    approvers=[
        MPAApproverConfig("alice", mode="webhook",
                          webhook_url="https://approvals.internal/alice"),
        MPAApproverConfig("bob",   mode="webhook",
                          webhook_url="https://approvals.internal/bob"),
        MPAApproverConfig("charlie", mode="webhook",
                          webhook_url="https://approvals.internal/charlie"),
    ],
))

client = HardenedX402Client(payment_signer=signer, mpa_engine=mpa)

Approval endpoints receive a POST with payment details and must return {"approved": true}. For machine-to-machine approvals, use mode="crypto" with HMAC-SHA256 countersignatures and pass them via mpa_signatures={"approver_id": "hex_sig", ...} in the request kwargs.


Prometheus Metrics (v0.3.0)

from presidio_x402 import HardenedX402Client
from presidio_x402.metrics import MetricsCollector

collector = MetricsCollector()
client = HardenedX402Client(payment_signer=signer, metrics_collector=collector)

# Expose /metrics endpoint (e.g., FastAPI)
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST

@app.get("/metrics")
def metrics():
    return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)

Available metrics: x402_payments_total, x402_payment_amount_usd (histogram), x402_pii_detections_total, x402_policy_violations_total, x402_replay_detections_total, x402_mpa_events_total.


Policy-as-Code (v0.3.0)

Define spending policy in a TOML or JSON file and validate it against the x402 policy JSON Schema:

# policy.toml
max_per_call_usd = 0.10
daily_limit_usd  = 5.00
agent_id         = "my-agent"

[per_endpoint]
"https://premium-api.io" = 0.50

[mpa]
threshold       = 2
min_amount_usd  = 1.00
timeout_seconds = 30

[[mpa.approvers]]
approver_id = "alice"
mode        = "webhook"
webhook_url = "https://approvals.internal/alice"
from presidio_x402.x402_policy_schema import load_policy_file

policy = load_policy_file("policy.toml")
client = HardenedX402Client(payment_signer=signer, policy=policy)

Remote Screening (v0.4.0)

Offload PII analysis to the hosted screen.presidio-group.eu service. Useful when you want to keep the agent process small (no in-process spaCy model) and centralise screening policy across many agents.

from presidio_x402 import HardenedX402Client

client = HardenedX402Client(
    payment_signer=signer,
    remote_screening=True,
    screening_api_key="...",   # obtain by registering at screen.presidio-group.eu
)

The free tier serves the regex-mode screening backend at 100 screenings/day per key. Falls back to local regex / NLP modes if the network is unhealthy, so the client never hard-fails on a screening-service outage.

Per-origin wallet allowlist (v0.4.0)

Defence against DNS-poisoning attacks that substitute the recipient wallet in a 402 response. Configure the trusted pay_to addresses per resource origin:

client = HardenedX402Client(
    payment_signer=signer,
    trusted_wallets={
        "https://api.example.com": {"0xAbC...123"},
    },
)

Payments to a pay_to not in the allowlist for the response's origin are blocked before signing.


Kubernetes Deployment (v0.3.0)

Deploy as a sidecar using the bundled Helm chart:

helm install x402 ./helm \
  --set x402.agentId=my-agent \
  --set x402.maxPerCallUsd=0.10 \
  --set x402.dailyLimitUsd=5.00 \
  --set serviceMonitor.enabled=true

See docs/soc2-reference-architecture.md for SOC 2 TSC mapping, GDPR obligations, and deployment patterns.


SLO Payment Broker (v0.7.0)

Market-based SLO enforcement: when infrastructure degrades, the agent autonomously pays for a capacity upgrade via x402 micropayments instead of relying on pre-provisioned autoscaling. The broker acts only on a verified degradation signal — a signed evidence-ref@1 from a trusted presidio-hardened-arch-translucency signer — so a spoofed or misconfigured signal cannot trigger a payment (authorization, not metric).

from presidio_x402 import (
    HardenedX402Client, SLOPaymentBroker, SLOPaymentPolicy,
    X402CapacityProvider, ArchTranslucencyAdapter,
)

# arch-translucency's signed degradation feed → verified trigger (fail-closed).
adapter = ArchTranslucencyAdapter(
    {"presidio-hardened-arch-translucency": {"alg": "ed25519", "public_key": ARCH_PUB}},
    expected_signers=["presidio-hardened-arch-translucency"],
)
client = HardenedX402Client(payment_signer=signer)
broker = SLOPaymentBroker(
    client=client,
    slo_policy=SLOPaymentPolicy(
        cooldown_seconds=300,            # anti-drain
        max_per_slo_event_usd=0.50,
        max_daily_slo_usd=10.00,
        tier_escalation_rules=(1.0, 2.0, 4.0),  # step-up pricing for repeated degradation
    ),
    provider=X402CapacityProvider("compute", "https://compute.example/v1/capacity", client),
    base_event_usd=0.10,
)

for trigger in adapter.build_triggers(signed_envelope):  # verified or skipped
    await broker.handle_trigger(trigger)   # → paid / blocked / skipped, with audit event

The signing key lives in a separate bridge sidecar (arch-translucency stays key-less); x402 holds only the public key. The wire contract is pinned by the evidence-ref@1 golden-vector tests shared with arch-translucency.


Decision-ref emission (payment-decision@1)

A decision-ref is a signed, portable record of one payment decision that a third party verifies offline — without re-running the gateway. It binds the per-control gate verdicts (pii → trusted_wallet → policy → replay → mpa), the hashed inputs, and the effective policy hash into the family evidence-ref@1 envelope, with a thin, recomputable decision_ref correlation id. Emission is opt-in and off by default; when no emitter is configured, behaviour is byte-identical to prior releases and no decision-ref code runs. There is no network I/O on the emit path.

from presidio_x402 import HardenedX402Client, DecisionRefEmitter, verify_decision_ref
from presidio_x402.decision_ref import FileDecisionRefWriter

# Signer independence is claim-critical: a policy/approval identity distinct from the
# payment wallet — a self-signed record fails closed on verify (signer_equals_runtime).
emitter = DecisionRefEmitter(
    signing_key=POLICY_PRIVATE_KEY_HEX,          # Ed25519 (or an HMAC secret)
    signer="presidio-hardened-x402-policy",
    writer=FileDecisionRefWriter("/var/log/x402-decisions.jsonl"),
)
client = HardenedX402Client(payment_signer=signer, decision_ref_emitter=emitter)
# ... one signed payment-decision@1 record is emitted per paid payment.

# Verify offline against a pinned trust store (fail-closed, distinct reasons):
result = verify_decision_ref(envelope, {"presidio-hardened-x402-policy":
    {"alg": "ed25519", "public_key": POLICY_PUBLIC_KEY_HEX}})
assert result.ok and result.verdict == "ALLOW"

The verifier checks signature, hash integrity, that the recorded verdict re-derives from the recorded controls (verdict == f(controls) — the line between attested and admissible), that the signer is not the actor's own controller (self-approval fails closed), and — when the spending policy came from a capability chain (capability-grant@1) — parent linkage to the chain's terminal grant_hash. A minimal offline CLI is provided:

python -m presidio_x402.decision_ref envelope.jsonl trust-store.json

Honest bound. A decision-ref proves what the library concluded under a declared predicate, tamper-evidently and recomputably. It does not prove the process was uncompromised or that the declared controls are the real controls (no TEE claim). Wire format and decision_ref derivation are pinned by the conformance fixture under tests/conformance/decision-ref/ and the design note plan/presidio-evidence-decision-ref-design.md.

Treasury binding (settlement-ref@1) — v0.10.0

A decision-ref says why a payment was allowed. Reconciling it into a ledger's close also needs which settlement it authorized — and the shipped payment-decision@1 record carries no transaction hash. settlement-ref@1 is that join: a signed, off-chain statement, under the same policy signer, committing to {decision_ref, chain (CAIP-2), tx_hash, block_number, log_index}.

from presidio_x402 import FileSettlementWriter, HardenedX402Client

client = HardenedX402Client(
    payment_signer=signer,
    decision_ref_emitter=emitter,                       # as above
    settlement_writer=FileSettlementWriter("settlements.jsonl"),
)
# The paid response's PAYMENT-RESPONSE echo is parsed and correlated with this
# payment's decision_ref. It carries the tx hash and network — NOT a block number
# or log index, so the record is written with "complete": false and those two
# fields are completed by the operator from a chain lookup.
python -m presidio_x402.treasury_binding export decision.json \
    --settlement settle.json --trust trust.json --key-file policy.key > bundle.json
python -m presidio_x402.treasury_binding verify bundle.json trust.json   # exit 0 iff verified

Export is fail-closed: it re-runs the full decision-ref verification (signature included — which is why --trust is required) and refuses a non-verifying envelope, a non-terminal REFER verdict, an out-of-bound identity string, or a join signed by an identity other than the decision's own signer. The bundle carries no key material: trust_store_ref names the signer and key id, and the verifier resolves both against its own pinned store — a bundle-supplied key would make verification trust-on-first-use.

Honest bound. This is an off-chain join, not an on-chain anchor: an ERC-3009 settlement's calldata carries no decision digest, so there is nothing on chain to check it against. It asserts only what the signer can — this decision, that transaction, signed. Bundle shape, ingest contract and residual risks: docs/treasury-binding.md; the cross-language byte contract: tests/conformance/treasury-binding/.

Provisioning Metadata Redaction

Capacity-upgrade requests can reveal workload type, data classification, or access pattern. Those heuristics are intentionally opt-in so ordinary payment metadata keeps the v0.6 false-positive profile.

from presidio_x402 import PIIFilter, PROVISIONING_ENTITIES

filt = PIIFilter(
    entities=list(PROVISIONING_ENTITIES),
    redaction_template="<{entity_type}>",
)

clean, entities = filt.scan_and_redact(
    "ml training workload over CONFIDENTIAL data: SELECT email FROM customers"
)
# clean:
# "<WORKLOAD_CLASS> workload over <DATA_CLASSIFICATION> data: <QUERY_PATTERN>"

Exceptions

Exception Raised when
PIIBlockedError PII detected in metadata and pii_action="block"
PolicyViolationError Payment amount or aggregate spend exceeds configured limit
ReplayDetectedError Payment fingerprint matches a recent transaction
X402PaymentError Upstream payment signing or network error
MPADeniedError Multi-party authorization required but not enough approvals received
MPATimeoutError Multi-party authorization webhook approval timed out

All exceptions are importable from presidio_x402.


Research Artifacts

Artifact Location Description
Synthetic corpus vstantch/x402-pii-corpus on Hugging Face · corpus/ 2,000 labelled x402 metadata triples; generator (generate.py, seed=42) + metadata (corpus_meta.json); raw JSONL reproducible from seed
Precision/recall sweep experiments/ 42-configuration grid search (run_sweep.py); latency benchmark (run_latency.py)
Dune Analytics queries dune/ 6 Trino SQL queries used to characterise the live x402 ecosystem (20 projects, 96 wallets, 11 chains, ≥79M transactions); see dune/README.md

Roadmap

Version Milestone
v0.1.0 PII redaction + spending policy + replay detection
v0.2.0 Synthetic corpus + 42-configuration precision/recall sweep, LangChain/CrewAI adapters, compliance report · arXiv:2604.11430
v0.2.1 Live ecosystem characterisation via Dune Analytics (20 projects, 96 wallets, 11 chains, ≥79M transactions); IEEE S&P magazine article submitted; IEEE TIFS paper under review · Corpus: v0.2.1/dataport/, Hugging Face, IEEE DataPort (doi:10.21227/kpsz-nq73)
v0.3.0 Multi-party authorization (mpa.py: n-of-m, webhook + crypto modes) · Policy-as-code JSON Schema (IETF draft candidate) · Prometheus metrics exporter · Kubernetes Helm chart + Docker image · SOC2 reference architecture
v0.4.0 Screening API launch — hosted screen.presidio-group.eu free tier (regex mode, 100 req/day) · ScreeningClient + remote_screening=True mode · per-origin pay_to allowlist (chain-06 mitigation) · audit-cycle hardening (F-A/B 2026-05-03, F-C/D/E 2026-05-10, F1/F2/F3 2026-05-17); see CHANGELOG.md
v0.5.0 OEM embed kit — rail-agnostic ScreeningPipeline core + bindings/x402 layer (PaymentProtocolBinding protocol, hedge against rail fragmentation: ACP/Tempo, AP2) · SEMVER.md stability guarantees · partner conformance suite (python -m presidio_x402.conformance) · CDP/LangChain/CrewAI quickstarts · SBOM in CI · freshness-bound MPA countersignatures (F-8)
v0.6.0 Production hardening + evidence substrate — multi-tenant replay namespaces · enterprise audit sinks (S3 / Splunk / Datadog) · signed evidence-ref@1 verification · SLSA/OIDC release provenance · digest-pinned Docker base + hash-pinned spaCy model · latency SLO and all-matrix coverage CI gates · OTel spans · policy hot-reload · startup key gates · human-pentest retest closure
v0.7.0 SLO payment broker (library) — x402 micropayments as runtime infrastructure bids: SLOPaymentBroker + SLOPaymentPolicy + evidence-anchored ArchTranslucencyAdapter + provisioning PII entities; cross-repo validated against presidio-hardened-arch-translucency. cs.DC preprint + empirical eval tracked separately
v0.8.0 PII completeness + supply-chain hardening (library) — nlp mode is now a structural superset of regex (no structured-identifier misses) · composed-envelope screen_ref leg · action_ref byte-determinism (NFC + non-empty entity sets) · URL-redacted log hygiene · OpenSSF Scorecard workflow/badge + SLSA Build L3 provenance · independent multi-round security audit cleared
v0.10.0 Treasury bindingsettlement-ref@1 signed off-chain join record + fail-closed bundle export/verify CLI · client-side settlement-receipt capture · caller-identity value bounds · cross-language conformance vectors (non-ASCII escaping, lone surrogates, non-string keys, i64 bounds, depth 128/129) — latest release
v0.9.1 Security patch — dependency-audit floors for click / setuptools, missing-payment-header log exposure hardening, and setup-uv v8.3.2 CI action pin
v0.9.0 Proof-carrying x402 evidence — capability-grant@1 attenuable spending grants + opt-in payment-decision@1 decision refs with raw-offer digest binding, fail-closed offline verification, capability-chain provenance linkage, and PII-free emitted records
Future Deferred #23 prompt-injection / agent-bound response scanning threat-model work

Planned direction (next 12 months)

Intended direction, not a commitment — priorities shift with the ecosystem and security findings:

  • Toward v1.0 — stabilise the public API under SEMVER.md and clear the mainnet release gate (KYB / signing / legal controls) for the paid screening route.
  • Prompt-injection / agent-bound response scanning (deferred issue #23) — extend the threat model beyond outbound payment metadata to untrusted agent-facing responses.
  • Broader rail coverage — additional PaymentProtocolBinding implementations as agentic payment rails consolidate.
  • Evidence and conformance — grow the partner conformance suite and the proof-carrying evidence vocabulary alongside upstream standardisation.

See PRESIDIO-REQ.md for full deliberation and rationale.


Contributing and support

  • Bug reports and feature requests — open a GitHub issue.
  • Security vulnerabilities — do not open a public issue; follow the private reporting process in SECURITY.md.
  • Code contributions — see CONTRIBUTING.md for the pull-request process, coding standards, and the test policy. Participation is governed by the Code of Conduct.
  • Architecture and governanceARCHITECTURE.md describes the components, payment flow, and trust boundaries; GOVERNANCE.md covers the governance model, roles, and project continuity; ASSURANCE.md is the security assurance case.

Install from PyPI: pip install presidio-hardened-x402.


License

MIT


SDLC

This repository is developed under the Presidio hardened-family SDLC: https://github.com/presidio-v/presidio-hardened-docs/blob/main/sdlc/sdlc-report.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

presidio_hardened_x402-0.10.1.tar.gz (565.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

presidio_hardened_x402-0.10.1-py3-none-any.whl (157.5 kB view details)

Uploaded Python 3

File details

Details for the file presidio_hardened_x402-0.10.1.tar.gz.

File metadata

  • Download URL: presidio_hardened_x402-0.10.1.tar.gz
  • Upload date:
  • Size: 565.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for presidio_hardened_x402-0.10.1.tar.gz
Algorithm Hash digest
SHA256 b823f0183cf7f6f4ece572db8766e3554d76bfbf856d083fd62733600296f957
MD5 c6d5e81aaafd7b8a13e3b708bd176201
BLAKE2b-256 bd318c979b485ca2248b990d3c467fcdb9415b559423d2474cf4792988687bde

See more details on using hashes here.

Provenance

The following attestation bundles were made for presidio_hardened_x402-0.10.1.tar.gz:

Publisher: publish.yml on presidio-v/presidio-hardened-x402

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file presidio_hardened_x402-0.10.1-py3-none-any.whl.

File metadata

File hashes

Hashes for presidio_hardened_x402-0.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e0820f34023c5539d18c0f0b227b2d4efc989ceb60b4728a762f3985623eb433
MD5 8a184b0c5aeafd2b5cd4f85ec28f0770
BLAKE2b-256 6b83c1d883b84fb34ac8a1368c898b76db6ca29c37e4c7aecb61c79ed3133482

See more details on using hashes here.

Provenance

The following attestation bundles were made for presidio_hardened_x402-0.10.1-py3-none-any.whl:

Publisher: publish.yml on presidio-v/presidio-hardened-x402

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page