Skip to main content

streamgine — Python SDK

Diff by Streamgine lives under the diff module. Install once; add more Streamgine modules (training data, etc.) under the same package later.

This directory is the customer package published to PyPI. Repo-only load tests and dev agents live in tools/python/ — not shipped to customers.

Today: one wheel streamgine = Diff (from streamgine import diff). Next products (e.g. training-data) get their own wheels (streamgine-diff, streamgine-training, …) under the same streamgine import namespace — see PUBLISHING.md and .cursor/rules/python-client.mdc. Do not grow a fat single package.

Product: diff.streamgine.com · Docs: docs.md · California: coverage · Use cases: risk monitoring, AI agents · Repo: github.com/scurtutech/diff-events-service

from streamgine import diff

# Once at startup — pick ONE download from /account:
diff.configure("diff-streamgine-credentials.json")
# OR: source diff-streamgine-env.sh  then  diff.configure()
client = diff.DiffClient()

Install from PyPI:

python3 -m pip install streamgine

Or from this repo:

cd clients/python
python3 -m venv .venv && source .venv/bin/activate
python -m pip install .

Requires Python 3.10+. One dependency: httpx (TLS verification enabled by default).

Credentials (one setup point)

Call diff.configure() once at process startup. Every DiffClient() and verify_webhook() reads from that config.

Download from /account or checkout — filenames are fixed:

Download Then
diff-streamgine-credentials.json diff.configure("diff-streamgine-credentials.json")
diff-streamgine-env.sh source diff-streamgine-env.sh then diff.configure()

Option A — credentials JSON:

diff.configure("diff-streamgine-credentials.json")

Option B — env script:

source diff-streamgine-env.sh
diff.configure()

Do not pass API keys or signing secrets to DiffClient() or verify_webhook() in normal use.

Zero-setup agent (copy-paste)

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e . fastapi uvicorn
# Customer path: download from /account, then ONE of:
#   source diff-streamgine-env.sh
#   # or keep diff-streamgine-credentials.json next to the process
# Local repo seed (AUTH_ENABLED): npm run seed:dev-customer, then export printed keys
source diff-streamgine-env.sh   # or configure("diff-streamgine-credentials.json") in code
python -m uvicorn examples.agent:app --port 8000

Runnable source: examples/agent.py. Calls register_test() on startup; within ~1s you should see after={'test': 'success'} on topic diff-event.test.

Serverless (AWS Lambda): examples/serverless/ — deploy handler.py, run register.py once, receive test heartbeats in CloudWatch.

Agent journey (production filters): examples/agent_journey.py.

Docker worker reaching a host agent:

export DIFF_CALLBACK_URL=http://host.docker.internal:8000/webhooks/diff

Test heartbeats (start here)

Before wiring real registry events, confirm your agent receives signed webhooks:

from streamgine import diff

diff.configure()  # after: source diff-streamgine-env.sh  OR  configure("diff-streamgine-credentials.json")
client = diff.DiffClient()
client.register_test(callback_url="https://your-agent.example/webhooks/diff")

Filter on event.is_test_heartbeat (or after == {"test": "success"}). The service sends this every second:

{
  "action": "INSERT",
  "after": { "test": "success" }
}

No entity_id on test events.

Quick start (production registry events)

from streamgine import diff

diff.configure()
client = diff.DiffClient()
client.register(
    match={
        "state": "CA",
        "principal_city": "San Francisco",
    },
    actions=["INSERT"],
    callback_url="https://your-agent.example/webhooks/diff",
)
client.close()

INSERT = newly added company, UPDATE = changed, DELETE = removed. Omit actions to receive every matching state/city event.

Search current companies (full story)

Webhooks deliver changes. Search returns current snapshots:

from streamgine import diff

diff.configure()
client = diff.DiffClient()
result = client.search(
    query={"match": {"principalCity": "San Francisco"}},
    size=10,
)
for company in result["entities"]:
    print(company.get("entity_name"), company.get("entity_number"))

Agent journey: Search → Watch → Qualify → Act → Stay current (see /docs.md).

2. Verify inbound webhooks (required)

Always verify raw request bytes before trusting JSON. The worker signs the exact JSON body with HMAC-SHA256.

from streamgine import diff

diff.configure()

def handle_webhook(raw_body: bytes, signature: str | None):
    try:
        event = diff.verify_webhook(raw_body, signature)
    except diff.WebhookVerificationError:
        return 401, {"error": "invalid signature"}

    print(event.action, event.entity_id, event.after)
    return 200, {"ok": True}

FastAPI

from fastapi import FastAPI, Header, Request, Response

from streamgine import diff

app = FastAPI()

@app.on_event("startup")
def setup() -> None:
    diff.configure()

@app.post("/webhooks/diff")
async def diff_webhook(
    request: Request,
    response: Response,
    x_diff_signature: str | None = Header(default=None, alias="X-Diff-Signature"),
):
    raw = await request.body()
    try:
        event = diff.verify_webhook(raw, x_diff_signature)
    except diff.WebhookVerificationError:
        response.status_code = 401
        return {"error": "invalid signature"}

    # Your agent logic here
    return {"ok": True, "entity_id": event.entity_id}

Flask

from flask import Flask, request

from streamgine import diff

app = Flask(__name__)
diff.configure()

@app.post("/webhooks/diff")
def webhook():
    try:
        event = diff.verify_webhook(
            request.get_data(),
            request.headers.get("X-Diff-Signature"),
        )
    except diff.WebhookVerificationError:
        return {"error": "invalid signature"}, 401

    return {"ok": True, "action": event.action}

Security

Practice How this client helps
Verify every webhook diff.verify_webhook() uses hmac.compare_digest (timing-safe) on the raw body
Match worker algorithm Same as Node: HMAC-SHA256(secret, raw_json_bytes) → header X-Diff-Signature: sha256=<hex>
Parse JSON only after verify Tampered bodies fail before WebhookEvent is built
HTTPS in production DiffClient(verify_tls=True) (default); use https:// callback URLs
Keep secrets out of code One configure() from JSON or env — never scatter keys in constructors

Never log the signing secret or skip signature verification in production.

Credentials file (after checkout)

Download diff-streamgine-credentials.json (or diff-streamgine-env.sh) from /account / checkout. JSON format (version 1):

{
  "version": 1,
  "service": "diff.streamgine.com",
  "customer_id": "cus_…",
  "api_key": "de_live_…",
  "signing_secret": "whsec_…",
  "api_url": "https://diff.streamgine.com",
  "env": {
    "DIFF_API_KEY": "de_live_…",
    "DIFF_SIGNING_SECRET": "whsec_…",
    "DIFF_API_URL": "https://diff.streamgine.com"
  }
}
from streamgine import diff

diff.configure("diff-streamgine-credentials.json")
client = diff.DiffClient()
# diff.verify_webhook(...) uses the same signing secret automatically

Auth

Env (via configure()) Used for
DIFF_API_KEY register() / register_test() (customer Bearer)
DIFF_PROVIDER_API_KEY get_state() and provider ingest when provider auth is enabled

Register responses are slim (ok / subscription fields only — no Redis index maps).

API surface (streamgine.diff)

Symbol Role
configure() Load credentials once (JSON path or DIFF_* env)
get_config() Read configured ClientConfig
DiffClient register(), register_test(), health(), get_state()
verify_webhook() Verify signature + return WebhookEvent
DIFF_* env name constants API_URL_ENV, API_KEY_ENV, SIGNING_SECRET_ENV, …
WebhookEvent Parsed action, optional entity_id, optional after
WebhookVerificationError Invalid/missing signature or malformed payload
SIGNATURE_HEADER "X-Diff-Signature"

Direct imports also work: from streamgine.diff import DiffClient.

Development

cd clients/python
python -m pip install -e ".[dev]"
pytest

Download files

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

Source Distribution

streamgine-1.0.0.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

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

streamgine-1.0.0-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file streamgine-1.0.0.tar.gz.

File metadata

  • Download URL: streamgine-1.0.0.tar.gz
  • Upload date:
  • Size: 17.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for streamgine-1.0.0.tar.gz
Algorithm Hash digest
SHA256 76eefb4cc55f22dd035727dd9321066a77259130078f3fade383a51ef398c456
MD5 4ee84696a70a8b264a040a9b3ad52953
BLAKE2b-256 af014bff8d71c72a86bab77eab69a57eeac39aac0962b64ffa5f6b3c989a9f99

See more details on using hashes here.

Provenance

The following attestation bundles were made for streamgine-1.0.0.tar.gz:

Publisher: publish-python.yml on scurtutech/diff-events-service

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

File details

Details for the file streamgine-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: streamgine-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 12.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for streamgine-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bb84739d8cf1232381cee1a8231f71ab57e9414209beb0be11eaefd7efd2d06a
MD5 394eecb29cb26a01bfc12a68cfe15685
BLAKE2b-256 81c6b5d171d1065a5d5e165f024de9f3c184c379304a2b1979fa07c4f5ca075d

See more details on using hashes here.

Provenance

The following attestation bundles were made for streamgine-1.0.0-py3-none-any.whl:

Publisher: publish-python.yml on scurtutech/diff-events-service

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

Release history Release notifications | RSS feed

1.0.1

2 files

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page