Skip to main content

softechlog

Server-side Python SDK for Softechlog — drop-in user activity logging and customer-facing activity feeds for SaaS. Works with FastAPI, Django, Flask, or plain scripts. Fully typed.

Install

pip install softechlog

Quick start

# app/softechlog.py — initialize once, import everywhere
import os
from softechlog import Softechlog

log = Softechlog(secret_key=os.environ["SOFTECHLOG_SECRET_KEY"])  # stl_sk_… — server-side only

Track an event (sync — Django, Flask, scripts)

from app.softechlog import log

log.track(
    actor={"id": str(user.id), "name": user.name, "email": user.email},
    action="member.invited",                 # resource.verb
    target_type="workspace",
    target_id=str(workspace.id),
    target_name=workspace.name,
    metadata={"role": "admin"},              # any JSON, ≤ 4 KB
    session_id=request.session.session_key, # optional: groups events into a session
)

track() returns immediately: events are queued and sent by a background thread, so a slow or unreachable Softechlog can never stall your request handler. Call log.flush() before a short-lived process (a serverless function, a cron job) exits — log.close() and interpreter exit flush automatically. Pass background=False to send inline and get the created event back.

Track an event (async — FastAPI)

from fastapi import BackgroundTasks

@router.delete("/files/{file_id}")
async def delete_file(file_id: str, background: BackgroundTasks, current_user: User = Depends(get_current_user)):
    file = await File.get(file_id)
    await file.delete()
    # Runs after the response is sent — the client never waits on Softechlog.
    background.add_task(
        log.atrack,
        actor={"id": str(current_user.id), "name": current_user.name},
        action="file.deleted",
        target_type="file",
        target_id=file_id,
        target_name=file.name,
    )
    return {"deleted": True}

await log.atrack(...) directly is fine too when you want the event id back; it is bounded by timeout × (retries + 1) plus backoff.

track() / atrack() never raise — failures return None and log a warning (unless silent=True). Requests are retried on network errors, timeouts, 429 and 5xx with exponential backoff. Retries are safe: every event carries an Idempotency-Key, so an event the API stored before the response was lost is never recorded twice. Invalid actions and oversized metadata are rejected locally without a round-trip. Offset-less datetime timestamps are sent as UTC.

FastAPI middleware — record the end user's IP and User-Agent

Without it, the API only sees your server's address.

from softechlog.fastapi import SoftechlogMiddleware

app.add_middleware(SoftechlogMiddleware)   # honours X-Forwarded-For; pass trust_proxy_headers=False if not behind a proxy

Every track()/atrack() call made while handling a request now carries context={"ip_address": …, "user_agent": …} automatically. You can also pass context= explicitly anywhere.

Show a feed to your users

The <softechlog-feed> web component needs a feed token — a short-lived credential scoped to one user. Mint it on your server:

@router.get("/activity-token")
async def activity_token(current_user: User = Depends(get_current_user)):
    tok = await log.afeed_token(actor_id=str(current_user.id), ttl_seconds=3600)
    return {"token": tok.token, "expires_at": tok.expires_at}
<script src="https://softechlog.com/feed.js"></script>
<softechlog-feed feed-token="stl_ft_…"></softechlog-feed>

B2B? Show a customer's admin their whole account. Track with tenant_id=str(org.id), then mint log.feed_token(tenant_id=str(org.id)) — the token reads every event in that account and nothing else. Pass actor_id too to show one member within it.

feed_token() / afeed_token() raise SoftechlogError on failure.

Erase a user (GDPR right to erasure)

Stop sending events for the user first, then:

erasure = log.erase("user_123", mode="delete", reference="DSR-2026-0142")
if erasure.status != "completed":           # 202 → finishing in the background
    erasure = log.wait_for_erasure(erasure.id)
  • mode: "delete" (default) removes their events; "redact" keeps what happened and when but removes who, IP, device, metadata and targets.
  • target_types (default ["user"]): target types whose target_id is this user, so mentions in other people's events are scrubbed. [] leaves them alone.
  • reference: your ticket id — no personal data; it is stored and appears in the evidence event.
  • get_erasure(id), list_erasures(actor_id=, status=, limit=, cursor=), wait_for_erasure(id, timeout=60, interval=1) (raises on failed or timeout). Async: aerase, aget_erasure, alist_erasures, await_for_erasure.

The returned Erasure never contains the user id — store it with the ticket as your record. See softechlog.com/docs/erasure.

Tamper evidence

track() with background=False (and atrack()) return leaf_hash — the event's integrity leaf, sealed into a signed, hash-chained checkpoint within 5 minutes. Keep it for high-value events.

# Daily: pin the latest checkpoint somewhere you control (e.g. S3 Object Lock)
cp = log.latest_checkpoint()                 # None before the first seal

report = log.verify_integrity(pins=[(cp.seq, cp.hash)], expected_retention_days=90)
if not report.ok:
    print(report.failures)

Also: integrity_status(), list_checkpoints(after_seq=, limit=), checkpoint_bundle(seq), integrity_keys(), each with an a-prefixed async twin.

Verify offline — softechlog.integrity

pip install "softechlog[verify]"            # adds cryptography for Ed25519
from softechlog.integrity import verify_bundle, verify_chain

keys = log.integrity_keys()
page = log.list_checkpoints(limit=500)
verify_chain(page.checkpoints, keys, pins=my_pins)   # VerifyResult(ok, failures, warnings, counts)
verify_bundle(log.checkpoint_bundle(42), keys)

Bundles contain your events' IPs and user agents — handle them like an export. Encoding and threat model: softechlog.com/docs/integrity.

Errors

track() / atrack() never raise. Everything else raises SoftechlogError, with .status (HTTP status, or None for network errors) and .body.

Options

Option Type Default Description
secret_key str required Your stl_sk_… secret key
base_url str https://api.softechlog.com Override the API base URL
timeout float 5.0 Request timeout in seconds
retries int 2 Retries on network errors, 429, 5xx (backoff, honours Retry-After; idempotent)
silent bool False Suppress warnings on errors
background bool True Sync track() queues to a worker thread instead of sending inline
queue_size int 1000 Max queued events in background mode (oldest are kept, new ones dropped)
transport — — Custom httpx transport (tests)

The client keeps persistent HTTP connections; call log.close() / await log.aclose() on shutdown (or use it as a context manager).

Limits (enforced by the API)

  • action ≤ 200 chars, actor.id ≤ 255, metadata ≤ 4 KB with finite numbers; the softechlog. action namespace is reserved
  • 60 erasure requests / minute per project; 10 verify_integrity() / checkpoint_bundle() calls / minute per project
  • 1,000 events / minute per secret key
  • Free plan: 10,000 events / month (402 when exceeded)

MIT © FutureGenSystems

Metadata

Release files for softechlog 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for softechlog 1.3.0
File Size Uploaded
softechlog-1.3.0.tar.gz 31.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for softechlog 1.3.0
File Interpreter ABI Platform
softechlog-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 54.3 kB

Release files / softechlog-1.3.0.tar.gz

Download URL softechlog-1.3.0.tar.gz
Size 31.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c13f1cab755c313cb7f747196b25af1c5136cb27b48fa55dd9c304b70f1287ec
BLAKE2b-256 checksum
How to use checksums
b6afbbdee1720566b730e33b2ac35c80f7730739e461583324a20bb905bb9f1c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.6

Release files / softechlog-1.3.0-py3-none-any.whl

Download URL softechlog-1.3.0-py3-none-any.whl
Size 23.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a5f8f39fba76c32023e7ef2b57fea2d76fe9fa933045e3937b4f6c22bd779de
BLAKE2b-256 checksum
How to use checksums
70567b023b643b88b045405c317998953f4eeec30954bf0cb7b8f7fbdf52593a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.6

Release history Release notifications | RSS feed

This release

1.3.0 This release

2 release 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