Note: the umbrella
genblazepackage publishes as 0.4.1 in this wave (thev0.4.0tag is the CHANGELOG wave name, not the umbrella version). Runmake post-release VERSION=0.4.1after publish.
[0.4.0] - 2026-06-25
Security hardening (SSRF, URL-only asset verification), two new providers
(Hume Octave TTS, AssemblyAI STT), and a batch of connector patch-republishes
to update the genblaze-core>=0.3.4 floor.
Released package versions
genblaze(umbrella) 0.4.0 → 0.4.1 — patch republish to refresh extras
and connector floors. PyPI cannot overwrite an existing version; all changed
pins ship only when a new wheel is published.genblaze-core0.3.2 → 0.3.4 (URL-only asset verification hardening,
SSRF DNS pinning + redirect guard, fan-in failure propagation, sink lifecycle
fix, async preflight offload, error sanitiser, tenant-scoped step cache)genblaze-s30.3.2 → 0.3.4 (bumpsgenblaze-corefloor to 0.3.4;
region-probe close onS3StorageBackend, backendclose()shuts boto3
client to release thread pool connections onrun()teardown)genblaze-cli0.3.0 → 0.3.2 (bumpsgenblaze-corefloor to 0.3.4;
exposesverify_hash()and output-asset sha256 diagnostics)genblaze-replicate0.3.0 → 0.3.2 (bumpsgenblaze-corefloor to 0.3.4)genblaze-gmicloud0.3.1 → 0.3.2 (bumpsgenblaze-corefloor to 0.3.4)genblaze-hume— new at 0.3.1 (Hume Octave TTS provider)genblaze-assemblyai— new at 0.3.0 (AssemblyAI speech-to-text provider)genblaze-openai0.3.0 → 0.3.1 (DALL-E URL outputs materialised locally;
pinned-DNS download; bumpsgenblaze-corefloor to 0.3.4)genblaze-google0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-decart0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-elevenlabs0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-langsmith0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-lmnt0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-luma0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-nvidia0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-runway0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)genblaze-stability-audio0.3.0 → 0.3.1 (bumpsgenblaze-corefloor to 0.3.4)
Security
genblaze-core0.3.2 → 0.3.4:Manifest.verify()now rejects output
assets that lacksha256for every supported schema version, preventing a
schema downgrade from bypassing declared output sha256 coverage. This is an
intentional security exception to the normal patch-release compatibility
policy; useverify_hash()for legacy hash-only checks against historical
URL-only media (#77).genblaze-core:Asset.sha256values remain loadable even when malformed,
so historical and cross-producer manifests can still be inspected with
verify=Falseorallow_unverified_assets=True.Manifest.verify()and
genblaze verifytreat missing, uppercase, or otherwise malformedsha256
as unverified output coverage. They do not fetch remote asset URLs; consumers
must independently hash fetched bytes before trusting those bytes (#77).genblaze-core: schema 1.6 URL-only hash markers are Python read-supported.
The canonical marker URL strips known credential, expiry, and response
override query parameters for AWS SigV4/SigV2, GCS, B2, CloudFront, Azure SAS,
and GCS V2 signed URLs (including bareauthorizationtokens) while retaining
resource-identifying query parameters. Default manifest emission, storage
writes, media embedding, and the published JSON Schema/TypeScript spec stay on
schema 1.5 for an expand-contract rollout (#77).genblaze-openai:dall-e-2/dall-e-3URL responses are now downloaded
immediately to a localfile://asset with a populatedsha256and
size_bytes, instead of being stored as the raw (credential-bearing,
~1-hour) Azure SAS URL. The signed URL is used only for that fetch and never
reaches the manifest, step cache, or any sink. This fixes the.run(sink=...)
transfer path (which previously received a credential-stripped, unfetchable
URL) and lets DALL-E outputs verify without a storage sink (#77).genblaze-cli0.3.0 → 0.3.2: raises itsgenblaze-corefloor to the first
core version that exposesverify_hash()and output-asset sha256 diagnostics
(#77).genblazeumbrella package: raises itsgenblaze-corefloor to 0.3.4 so
umbrella installs receive the verification hardening (#77).- Provider and storage connector packages now require
genblaze-core>=0.3.4,<0.4
and carry patch version bumps so adapter-only installs receive republished
wheels with the URL-only asset verification fix (#77). ObjectStorageSink.read_manifest_for_asset()now requirestenant_id, stores
tenant-scoped asset index entries, validatesasset_idas a UUID, falls back
to legacy flat index entries during migration, rejects manifest pointer
substitution unless the recovered manifest references the requested asset,
and applies the same staged verification behavior asread_manifest()(#77).ObjectStorageSink.write_run()now fails the write when an asset transfer
fails, instead of uploading a success-path manifest that later fails strict
verification. Successful transfers from a partial failure are reused on
retry and the in-memory manifest hash is recomputed before raising (#77).
Changed
ObjectStorageSink.read_manifest(verify=True)verifies canonical hashes by
default and logs output assets whosesha256is missing or malformed.
Hard-failing those unverified outputs is staged behind
strict_manifest_reads=TrueorGENBLAZE_STRICT_MANIFEST_READS=trueso
operators can backfill historical URL-only manifests before enabling strict
read failures on hot paths (#77).genblaze-core[audio]extra: mutagen capped at<1.49. Mutagen 1.48
changed m4a timescale handling in a way that broke the AAC handler test
fixture; the cap prevents a future mutagen release from reintroducing
the same break before it can be vetted (#108).
Fixed
tools/check_pin_parity.py: extend the pre-publish drift guard to
compare[project.optional-dependencies]as well as base
[project.dependencies]. Previously the gate reported
0 driftfor thegenblazeumbrella even when connector pins in
theall,video,image, oraudioextras diverged from the
published wheel, becauseRequires-Distextras entries were
silently filtered out. The gate now groups PyPI extras by name and
diffs each extra independently, naming the extra in the drift
report (e.g.[all]). Addstools/tests/test_check_pin_parity.py
and wirespytest tools/tests/intomake test. Updates
RELEASING.md to accurately describe the gate's scope (#23).
Added
genblaze-hume: new provider adapter for Hume AI Octave TTS (audio /
text-to-speech). SynchronousSyncProviderthat decodes the API's base64
audio to a localfile://asset;step.model(octave-1/octave-2)
maps to the Octaveversionfield. Ships a pattern-keyedoctave-*model
family with a permissive fallback,DiscoverySupport.NONE, and no hardcoded
pricing (register per-character rates via the recipe in
docs/reference/pricing-recipes.md). Available aspip install genblaze-hume
or thegenblaze[hume]/genblaze[audio]extras.genblaze-assemblyai: new provider adapter for AssemblyAI speech-to-text
/ transcription — the first connector that consumes audio and produces
text. AsyncBaseProvider(submit / poll / fetch_output) that resolves an
audio URL (step.inputs[0]→params["audio_url"]→prompt, SSRF-validated
viavalidate_chain_input_url) and emits a hash-verified TEXT asset
(text:{sha256},media_type="text/plain", transcript inmetadata["text"])
with word-level timings onAudioMetadata.word_timings(converted ms → s).
step.modelis sent on the SDK's pluralspeech_modelsfield (the live API
has deprecated the singularspeech_modelfield and the legacy best/nano
aliases). Ships a pattern-keyedassemblyai-speechfamily (universal-3-pro
/universal-2) with a permissive TEXT fallback,
DiscoverySupport.NONE, and no hardcoded pricing (register
per-minute-of-input-audio rates via the recipe in
docs/reference/pricing-recipes.md). Available as
pip install genblaze-assemblyaior thegenblaze[assemblyai]extra.
Security
genblaze-core: DNS pinning closes the rebinding / TOCTOU window on all
outbound HTTP paths.resolve_ssrfresolves the hostname once, validates
every returned IP, and returns the pinned address. Callers connect to that
IP directly — the HTTP client never performs a second independent resolution.
TLS SNI and cert verification continue to use the original hostname. Affects
storage/transfer.py(urllib3HTTPSConnectionPoolper hop), and
webhooks/notifier.pyandgenblaze_openai/dalle.py(direct
http.client.HTTPSConnectionwith pre-connected pinned socket) (#9).genblaze-core: SSRF guard now validates every HTTP redirect hop in the
asset transfer path (storage/transfer.py). Previously,check_ssrfran
only on the initial URL; a CDN redirect to a private/loopback/IMDS address
bypassed the guard entirely. Redirects are now followed manually with a
bounded loop (max 5 hops); eachLocationis re-resolved and re-pinned
before following, and downgrade to non-HTTPS is rejected (#9).genblaze-core: Webhook delivery (webhooks/notifier.py) switched from
urllib.requesttohttp.client.HTTPSConnectiondirectly.http.client
has no redirect handler, so a 3xx response is treated as a delivery failure
rather than following theLocationheader, preventing a server-side
redirect to an internal host from bypassing the SSRF guard (#9).genblaze-openai:_download_https_to_tempnow useshttp.client
directly with a pinned DNS connection, closing both the redirect-bypass and
DNS rebinding vectors for edit-input downloads (#9).genblaze-core: IPv4-mapped IPv6 addresses (::ffff:169.254.x.x,
::ffff:10.x.x.x, etc.) are now normalized to their IPv4 form before the
SSRF blocklist check. Previously, a DNS response returning an IPv4-mapped
address bypassed all IPv4BLOCKED_NETWORKSentries (#9).genblaze-core: HTTPLocationheaders in redirect chains are now resolved
withurljoinbefore re-validation, so RFC-legal relative redirects work
and relative redirects to private targets are still rejected (#9).- Egress proxy note: DNS pinning requires a direct TCP connection to the
validated IP;HTTP_PROXY/HTTPS_PROXY/NO_PROXYenv vars are ignored
by design on all pinned outbound paths (transfer, webhook, dalle). Deployments
that require an egress proxy should allowlist the target hosts at the proxy
instead of relying on env-var forwarding.
Fixed
genblaze-core:Pipeline.arun()no longer blocks the event loop during
model preflight. The network-bound phase (_validate_models, which uses a
ThreadPoolExecutorfor provider discovery fetches) is now offloaded via
asyncio.to_thread, allowing concurrent coroutines to keep running during
the preflight window. Cheap capability checks (modality, chain-input) still
run synchronously.Pipeline.run()behavior is unchanged (#56).genblaze-core:Pipeline.run()/arun()now release a sink's run-scoped
resources in theirfinallyblock (and on early preflight/validation
failure), shutting down theObjectStorageSinkeager-upload
ThreadPoolExecutorand releasing the backend connection pool —
S3StorageBackend.close()now closes the boto3 client. Previously non-daemon
worker threads stayed alive afterrun()returned and in-flight eager-upload
futures leaked on error paths that bypassedwrite_run. Sinks declare
lifecycle ownership viaBaseSink._close_with_run(defaultTrue):
run-scoped sinks (ObjectStorageSink) are closed by the pipeline and are
single-use, while fire-and-forgetWebhookSinkopts out (False) so it is
never closed — keeping webhook delivery non-blocking and the sink reusable
across runs.batch_run()/abatch_run()close their shared sink once after
the whole batch rather than after the first item.BaseSinkgains
__enter__/__exit__for callers that manage the lifecycle outside a
run(). Behavior change: a run-scoped sink passed torun()/arun()is
closed afterward — construct a fresh one per run rather than reusing it (#57).genblaze-openai,genblaze-google: remove hardcoded USD per-token
rate tables (_RATES) from the standalonechat()helpers and return
cost_usd=Noneunconditionally, consistent with the 0.3.0 contract that
connector modules ship zero static rate tables (Pipeline-Step providers
register rates viaPricingContext/ModelSpec). The standalonechat()
helpers have no model registry, so callers that relied on
ChatResponse.cost_usdbeing non-Nonefor known models must now compute
cost fromtokens_in/tokens_outwith their own rates (see
docs/reference/pricing-recipes.md). Adds
test_pricing_phaseout.pytogenblaze-core— a lint-style CI guard that
fails if any future connector reintroduces a_RATES/_PRICINGconstant (#13).genblaze-core: post-submit step-level retries now resume the existing
upstream prediction instead of submitting a new one, including transient
checkpoint failures aftersubmit()returns by replaying idempotent
on_submit(step_id, prediction_id)callbacks before retry resume (#70).genblaze-core: fan-in consumers usinginput_fromnow fail before provider
invocation when a referenced producer step failed, produced no assets, or
points at an out-of-range prior step, preventing a downstream step from
reporting success with empty declared inputs (#69). Affected pipelines that
previously appeared green can now reportFAILED/INVALID_INPUT, which may
increase status-based alerts and change manifest hashes once during rollout.
Route or re-baseline those alerts using
metadata.failure_reason="input_resolution"; these pre-failed steps also
carrymetadata.provider_invoked=falsefor telemetry filtering.genblaze-core: failedStep.errorvalues now use the shared sanitizer for
OpenAI/Anthropic/Google/Replicate keys, AWS access key IDs and secret access
keys, Backblaze B2 application keys, JWTs, bearer/token headers, API-key
assignments, and basic-auth URL credentials, then truncate to 500 characters
before manifest, log, or stream-event emission. This redaction hardening ships
with #69 because fan-in pre-fail telemetry can otherwise re-surface untrusted
upstream provider errors on dependent steps.genblaze-core:StepCachenow partitions the step cache key bytenant_id
when a tenant is set (viaPipeline(tenant_id=...), or passed to
StepCache.get/put), so a cache shared across tenants no longer serves one
tenant's cached output asset to another for an otherwise-identical step.
Single-tenant keys are unchanged. Atenant_idinRunnableConfigis now
rejected at runtime (it was never honored), so a dynamic caller passing it via
config()/invoke(config=...)fails loudly instead of silently bypassing
cache isolation (#68).genblaze-replicate(0.3.0 → 0.3.1): declarehttpx>=0.24as a
direct dependency.ReplicateProvider._get_client()importshttpx
directly to build the client timeout, but the package declared only
replicate, which pinshttpx>=0.21— below the0.22floor where the
httpx.Timeout(connect=...)kwarg landed. Pin aligned with the
httpx>=0.24already used by the nvidia, gmicloud, and stability-audio
connectors. Version bumped so a corrected wheel can publish past the
skip-existingpin-parity gate (#37).genblaze-core(0.3.2 → 0.3.3): declareurllib3>=1.26,<3as a
direct dependency.storage/transfer.pyimportsurllib3unguarded at module load
(the sharedPoolManagerbehindAssetTransfer) andstorage/__init__
imports it eagerly, soimport genblaze_core.storagehard-required urllib3 on
a clean install while core declared onlypydantic+pillow. It previously
arrived only transitively via a connector's boto3 stack — the same
clean-install crash class as the replicate/httpx fix above.genblaze-s3(0.3.2 → 0.3.3): declarebotocore>=1.31(imported
directly inbackend.py) andaiobotocore>=2.7in theasyncextra
(imported directly inasync_backend.py). Both always shipped transitively
via boto3/aioboto3, which pin their exact versions — so these declarations add
honesty for the dependency gate without changing what resolves.
Added
genblaze-core:http(httpx),otel(opentelemetry-api), and
testing(pytest) extras. These advertise previously-undeclared
guarded/soft imports. In particulargenblaze_core.testing(the public
MockProvider/ProviderComplianceTestsharness) importspytestat module
load and ships in the wheel, so it now installs via
pip install "genblaze-core[testing]".
Changed
- Repo tooling: new
make deptrydependency-hygiene gate
(backed by per-package[tool.deptry]config) fails on undeclared imports
(DEP001), shipped imports of dev-only deps (DEP004), and misclassified
transitive deps — the clean-install crash class above. Wired intomake lint,
make pre-release, and a newdeptryCI job that also runspip check
against the editable workspace.libs/metais excluded (umbrella metapackage:
its deps are install-time bundles, not imports).