The upstream v2 routers mount under /v1 without collisions. flowweb
answers /api with 404 instead of the SPA shell, and a CI test fails
on any first-party /api/ or /v2/ path in src/.
Claude-Session: https://claude.ai/code/session_011i2AG9nwpiKUCVxAdDzYuU
It answered 403, so every anonymous visit logged a failed request before
the login page appeared. The answer is now 200 with auto_login false,
like /v1/session answers unauthenticated visitors, and useGetAutoLogin
reads it and sets autoLogin to false. A request that fails still clears
autoLogin and retries as before.
Co-authored-by: Hanzo Dev <[email protected]>
The Discord invite and member count pointed at EqksyE2EX9, the upstream
Langflow server; they now use CJCyAsm9Vr, the Hanzo server, in the UI, the
startup banner and the docs site.
The app fetched /v1/store/tags on every load although the store UI is
off, and the store host does not resolve, so each load logged four 500s.
The query now runs only when ENABLE_FLOW_STORE is on.
The welcome logo was offset onto the heading; it now sits above it.
Co-authored-by: Hanzo Dev <[email protected]>
The auto_login probe answers 403 when auto-login is off. The response
interceptor read that as an expired session, called /v1/refresh four
times, logged out and left an unhandled rejection; it now leaves the
probe to useGetAutoLogin, which already handles that answer.
GitHub stars, crash-report links and documentation links named
flow-ai/flow, flow.org and docs.flow.org, which belong to other
projects. They now name hanzoai/flow, hanzo.ai/flow and
docs.hanzo.ai/flow, as the backend components do. The help menu drops
the Flow Desktop download, which has no Hanzo build, along with the
unused isAuthorizedURL helper.
Co-authored-by: Hanzo Dev <[email protected]>
The backend mounts its API at /v1 and /v2, but flowweb forwarded /api/,
so every API call from the UI came back as the SPA shell. A routing test
covers API paths, SPA routes and the method guard.
Co-authored-by: Hanzo Dev <[email protected]>
The upstream merge left the backend unable to start: lfx.memory imported
has_flow_db_backend, which did not exist, and appended a second
import-time dispatch that named an unimported function. Around it, 17
modules referenced names whose imports were gone, several re-export
packages were empty or pointed at modules that no longer exist, and
settings/base.py still held the monolithic Settings class the groups
replaced (without cache_dir and mcp_base_url).
Each file now matches its upstream counterpart at the merge point, with
this fork's own changes kept: the /v1 and /v2 API paths, the org_id
columns, Hanzo IAM in auth, and product naming. Empty leftover packages
from the rename are removed, and the assistant flow file is
FlowAssistant.json to match FLOW_ASSISTANT_FLOW.
python -m flow run --backend-only starts on SQLite and answers /health,
/health_check and /v1/version.
Co-authored-by: Hanzo Dev <[email protected]>
hanzo.yml declares the image and a core Go gate for hanzoai/ci's build.yml,
called from .hanzo/workflows/cicd.yml on every push to main. The image
version is the project version in pyproject.toml, now 1.8.5.
The forge reads the first workflow directory that exists, so security.yml
and storybook.yml move beside cicd.yml and ask for linux-amd64, the label
forge runners advertise.
Co-authored-by: Hanzo Dev <[email protected]>
hanzoai/flow is the repository ghcr.io/hanzoai/flow builds from; the page
no longer describes it as a retired copy.
Co-authored-by: Hanzo Dev <[email protected]>
The forge reads .github/workflows natively and github.com reads nothing else,
so one directory serves both planes instead of two conventions that disagree
about which one is live. The reusable in hanzoai/ci already lives there, so a
caller naming that path now resolves to a file that exists.
Co-authored-by: Hanzo Dev <[email protected]>
The lane never scanned anything. `pip --break-system-packages` cannot
install semgrep on the runner image: semgrep wants PyJWT, the image has
PyJWT 2.7.0 from its distro, a distro package carries no RECORD, so pip
cannot uninstall what it needs to replace and stops. The flag grants
permission to touch that environment; it does not make the uninstall
possible. uv gives semgrep its own environment and shims the binary onto
PATH — the same fix the insights lane took, so both read alike now.
With the install working the scan reports four, and all four are the
code being careful rather than careless. Each carries the reason at the
line, because a bare suppression is indistinguishable from someone
silencing a real one:
alembic/env.py the advisory-lock key is an int on both branches —
a sha256 digest reduced mod 2**63-1, or a literal
auth/iam.py decoded unverified only to read `iss` and pick a
validator; a token claiming our issuer then goes to
validate(), which checks the signature and raises
auth/service.py reads a claim without authenticating it, safe while
nothing authorises on the result — today only this
module's tests call it
arxiv.py imports the Element TYPE for annotations; parsing is
defusedxml.fromstring, two lines below
Placement matters and cost a round trip: the directive has to sit on the
line the rule anchors to, which for the jwt call is the `options=` line
carrying verify_signature, not the line opening the call.
Co-authored-by: Hanzo Dev <[email protected]>
Every branch here is reachable from `hanzoai/flow` — 13 refs, not one commit it lacks — and this copy has no push mirror, so anything committed here reached nothing.
Every branch here is reachable from `hanzoai/flow` — 13 refs, not one commit it lacks — and this copy has no push mirror, so anything committed here reached nothing.
hanzoai/flow keeps ghcr.io/hanzoai/flow.
Every branch here is reachable from `hanzoai/flow` — 13 refs, not one commit it lacks — and this copy has no push mirror, so anything committed here reached nothing.
hanzoai/flow keeps ghcr.io/hanzoai/flow.
Every branch here is reachable from `hanzoai/flow` — 13 refs, not one commit it lacks — and this copy has no push mirror, so anything committed here reached nothing.
hanzoai/flow keeps ghcr.io/hanzoai/flow.
This action is written on git.hanzo.ai and mirrored to github.com. Naming the
host fetches it from where it is written rather than from a copy, and the
runner clones it with the forge's credential.
Co-Authored-By: Hanzo <[email protected]>
These comments named bin/sitedeploy, a path that stopped existing when the
action was renamed. A comment that points at nothing costs the next reader the
same search it just cost this one.
hanzoai/ci renamed .github/actions/sitedeploy to site and moved the floating v1
tag onto it, so this step pointed at a path that no longer exists.
act resolves every step's action before running step 0, so the job dies before
any step starts: each step reads cancelled at 0s while the job reads failure.
The reference link pointed at clickhouse.com under a Hanzo Datastore label.
docs.hanzo.ai/docs/datastore answers 200.
The slug stays: it is a published URL. versioned_docs are frozen snapshots of
what those releases shipped.
Datastore speaks the same wire, so this component already connects to it. What
it showed in the palette was the upstream's name.
name and icon stay: a saved flow references the component by name and the icon
by its registered key, so changing either orphans existing flows. The
langchain_community and clickhouse_connect imports stay too — those are the
published artifacts the runtime resolves.
This passed secrets.HANZO_DEPLOY_TOKEN, a forge org secret holding a key that had
stopped resolving, so cloud answered 403 on every publish. A secret nobody can
read back is a secret nobody can tell is dead. sitedeploy now reads the key where
secrets live, and this file passes the KMS client identity instead.
The empty-state hero carries the Hanzo block-H from hanzoai/logo,
rasterized at 512px for its 160px retina slot: the white variant on the
dark theme, the mono variant on the light one.
The app mark inherits currentColor so it follows the foreground in
either theme, and the accent color is a neutral.
The forge reads the first WORKFLOW_DIRS entry that exists. .hanzo/workflows has
existed here since the image lane landed, so all 43 workflow files under
.github/workflows ran nowhere. Nothing went red, because the checks that would
go red were the ones not running: the forge listed 2 workflows for this repo
against 45 files on disk. This repo has had no test gate of any kind since
actions-runner-controller was decommissioned.
security.yml replaces the two dead scanners rather than restoring them:
- codeql.yml ended in github/codeql-action/analyze under `security-events:
write`, which names an upload to GitHub's code-scanning store. There is no
such store here, and a scanner whose findings have nowhere to go is not a
scanner. Semgrep covers the same two languages CodeQL was configured for.
- mend.yml ran the Mend unified agent against WS_APIKEY / WS_USERKEY. That is
a commercial subscription this org does not hold. Dependency and licence
scanning is GONE, not replaced — semgrep reads code, not a dependency graph,
and claiming otherwise would be the more expensive mistake.
Measured before it was written: 98 rules over 3558 git-tracked files in 20s.
The other 42 files are upstream's CI and cannot run here: PR labelers, autofix
bots that push to pull requests, IBM Globalization Pipeline, Codeflash, macOS
and Windows release trains, PyPI publishes for packages this fork does not own,
and a `ci_success` gate that passes only when upstream's flow-nightly was
published to PyPI that same day. They are deleted rather than moved — a dead
file cannot accumulate where there is no directory.
Still ungated and needing their own change: mypy, which lint-py.yml ran and
which nothing ever called even upstream, and `ruff check`, which the tree fails
1204 times against its own declared config.
The enqueue -> upload -> complete calls were transcribed into this file and
twelve others before hanzoai/ci carried them. Four steps collapse to one
`uses:`, so the plane's contract is written once and a fix lands everywhere
rather than in whichever repo someone remembered to edit.
It also self-provisions. sitedeploy POSTs /v1/projects idempotently before the
enqueue, so a slug that has never deployed no longer 404s on its first run --
which is every site in this fleet except hanzo-ai, hips and hanzo-app.
Build steps are untouched, verified byte-identical against the previous
revision; only the Sites-plane plumbing moved.
Co-authored-by: Hanzo Dev <[email protected]>
Three site deploys in this repo published to planes that are not ours.
deploy-storybook.yml handed src/frontend/storybook-static to
actions/deploy-pages. It is a real static site, so it moves:
.hanzo/workflows/storybook.yml builds the export, POSTs
/v1/projects/flow-storybook/deploy, syncs the bytes straight to the S3 prefix
cloud names in the 202, then flips the deployment live — and marks it error if
the run dies after enqueueing, so cloud never records a deployment as "queued"
forever. Modelled on hanzoai/hips. It runs on hanzo-build-linux-amd64, a label
the forge runners actually advertise; a label nothing answers does not fail, it
queues silently until the 24h timeout.
This is a SEPARATE file from .hanzo/workflows/deploy.yml on purpose. That one
builds and pushes ghcr.io/hanzoai/flow — the App the operator runs. An App is a
container the cluster executes, a Site is bytes S3 serves; folding a docs
publish into an image build would couple it to a deployable release.
deploy_gh-pages.yml pushed docs/ to a gh-pages branch via
peaceiris/actions-gh-pages, and deploy-docs-draft.yml published per-PR docs
previews to an AWS S3 bucket behind CloudFront on upstream's account
(DOCS_AWS_ACCESS_KEY_ID, DOCS_DRAFT_CLOUD_FRONT_DISTRIBUTION_ID). Both are gone
with no native replacement, because this repo is not where these docs live:
hanzoai/docs carries them at apps/flow, and standing a second site up here would
be a second source of truth for one set of pages. docs/static/CNAME
(docs.flow.org) and docs/static/.nojekyll go with them — Pages artifacts that
mean nothing to S3.
The docs tree also shipped three third-party trackers: a Google gtag
(G-SLQFLQ3KPT), the consent-mode + TrustArc scripts feeding it, and a Segment
plugin reading SEGMENT_PUBLIC_WRITE_KEY. All removed, along with
@docusaurus/plugin-google-tag-manager, @mendable/search and
docs/src/plugins/segment. Telemetry goes to our own bus or nowhere.
Analytics on the storybook is the ONE hosted tag (cloud apps/analytics/tag.go)
posting to POST /v1/event. Its key is read from the project this run deploys, so
pk- rotation is one server-side change rather than five repo edits, and no key
means no tag: a keyless beacon is accepted 200 into $public, a tenant this org
cannot read.
Every other workflow stays. They are release, image-build, lint and test lanes,
not deploy planes.
Co-authored-by: Hanzo Dev <[email protected]>
An unused-import sweep ran against the wrong tree and deleted LOAD-BEARING
imports across the API layer, so main answered 500 NameError on first touch:
settings could not even construct (os, logger, copy2, AGENTIC_VARIABLES,
sanitize_database_url), create_app crashed on DeploymentGuardError, flows
CRUD lost its _new/_read/_patch/_update/_upsert helpers, projects lost its
deployment guards and MCP helpers, monitor lost the message crud, run lost
allow_custom_components (never lifted flat onto Settings) and builds lost the
Flow join. tracing/native.py additionally lost the resolved-spans collection
loop, so every trace flush hit an undefined name.
Repaired mechanically (every fix is an import restored to the module that
defines the name, plus the two Settings fields main.py reads: root_path,
allow_custom_components) and proven live: create -> list -> get -> patch ->
run (ChatInput->ChatOutput graph executes and echoes) -> monitor builds ->
delete, all 2xx against a fresh sqlite deployment. ruff --select F821 is
clean on every touched file.
Co-authored-by: Hanzo Dev <[email protected]>
The IAM seam comments credited Casdoor for behavior that is ours: Hanzo IAM
is original work, and the Casdoor-derived server (iam-v1) is retired and ships
in nothing. "Hanzo IAM is Casdoor-derived" was simply false.
Claim handling is unchanged — org still reads `owner` before `org`/`organization`,
and built-in/admin are still skipped as non-tenants. Only the attribution moves.
Co-authored-by: Hanzo Dev <[email protected]>
TypeScript 7 is the native Go compiler and removes `baseUrl` and
`moduleResolution: node|node10`. Both appear here, so `tsc` from TS7
refuses the config outright (TS5102 / TS5108) and cannot typecheck.
`paths` targets resolve relative to `baseUrl` when it is set and relative
to the tsconfig file otherwise. Every `baseUrl` folded here already
pointed at the config's own directory, so dropping it moves nothing and
the targets are left byte-identical. Where a baseUrl pointed elsewhere,
each affected target was rewritten as join(baseUrl, target).
`moduleResolution` was chosen from the declared `module`: commonjs ->
node16, esnext/preserve -> bundler. Configs whose `module` is unset or
exotic were left alone rather than guessed at.
The result is accepted by BOTH toolchains, so nothing has to upgrade
TypeScript in lockstep. Verified on hanzo/chat packages/api: tsc 5.9
779 -> 778 errors (no regression), and tsc 7.0.2 now runs the project
in 2s where it previously refused the config.
Co-authored-by: Hanzo Dev <[email protected]>
core/go.mod was bumped to `go 1.26.5` while the goweb stage stayed on
golang:1.23-bookworm. The golang images pin GOTOOLCHAIN=local, so the base can
never fetch a newer toolchain to satisfy the directive:
go: go.mod requires go >= 1.26.5 (running go 1.23.12; GOTOOLCHAIN=local)
The build dies in 0.086s. A release build that fails in under a second did not
fail to compile — it failed to acquire a toolchain.
This is the fourth instance of the same shape (search-fts5 golang:1.23 vs go
1.26.4, world node:20 vs pnpm@11, search rust:1.89 vs 1.91.1), so I swept the
whole repo: node:20-slim, python:3.12.12-slim-trixie and now golang:1.26 all
satisfy what the tree declares.
Worth noting the frontend is now clean — this build got all the way through
vite to a Go stage, which the asset and identifier fixes are what unblocked.
Co-authored-by: Hanzo Dev <[email protected]>
playground-modal.tsx carried the same import/usage split as the other eight
files — it imported FlowLogoColor from ../../assets/FlowLogoColor.svg and
rendered <HanzoFlowLogoColor>. The previous commit made the identifiers agree,
which is what finally let rollup reach the import and report that the FILE has
never existed either.
Collapsed onto FlowLogo, the asset that does exist. FlowLogoColor was a colour
variant of a monochrome mark used in an 18x18 slot, so one asset under one name
is both correct for the brand and one fewer thing to keep in sync. Also removed
the stale commented-out import above it, which pointed at a third spelling
(FlowLogocolor.svg) that never existed.
Removed pages/MainPage/pages/enchanced-beam-effect.tsx: imports BorderBeam from
../../../components/ui/border-beams, which is not in the repo, and nothing
imports it. Same shape as importButtonComponent.
Note both orphans were UNREACHABLE, so rollup never resolved them and neither
was blocking the build — that is cleanup, not the fix. Widened the sweep from
alias-only to alias + relative specifiers, which is what caught FlowLogoColor:
2778 -> 5495 checked, now 0 unresolved.
Co-authored-by: Hanzo Dev <[email protected]>
The previous commit renamed the asset FILES but shipped none of the 13 source
edits that point at them: git mv and git rm stage themselves, plain edits do
not, and that commit named no paths. So the tree kept importing
@/assets/HanzoFlowLogo.svg?react from an asset that is now FlowLogo.svg, and
rollup failed on exactly the import the rename was meant to remove.
It did move: modules transformed went 3401 -> 6441, so the earlier asset and
module renames were real. This is the remaining 8 files.
Co-authored-by: Hanzo Dev <[email protected]>
The rebrand sed rewrote import PATHS and IDENTIFIERS but never the files they
name, in three distinct ways. Rollup reports one unresolved import per build,
so these surface one at a time, each looking like a fresh unrelated breakage:
1. Assets renamed in code only. Restored the files to the names the code
already asks for:
langflow_assistant.svg -> flow_assistant.svg (6 importers)
langflow-icon-smooth.svg -> FlowLogo.svg (9 importers)
MCPLangflow.png -> MCPFlow.png (1 importer)
No file referenced the old names, so these are pure renames.
2. ONE logo, TWO invented names. The same asset was rewritten to both
FlowLogo.svg and HanzoFlowLogo.svg, so neither existed. Worse, the rename
landed on imports and usages independently — LoginPage imported FlowLogo
and rendered <HanzoFlowLogo>, an identifier bound to nothing. Collapsed to
FlowLogo everywhere (13 occurrences, 8 files); import and usage now agree.
3. Modules renamed in importers only:
custom-langflow-counts.tsx -> custom-flow-counts.tsx
langflow-counts.tsx -> flow-counts.tsx
Also removed modals/fileManagerModal/components/importButtonComponent/. It
imports @/components/ui/morphing-menu, which has never existed in this repo,
and it is orphaned: its only reference is inside a JSX comment and nothing
imports it. Every one of its three onClick handlers is an empty body with a
"// Handle X click" comment, so it is unfinished scaffolding, not a feature.
Writing MorphingMenu to satisfy it would ship a menu where every item does
nothing.
Verified by resolving all 2777 '@/' specifiers against the tree: 0 unresolved,
0 stale references to the renamed files.
Co-authored-by: Hanzo Dev <[email protected]>
vite.config.mts declares envHanzoFlow on line 22 but every USE of it reads
`envHanzo Flow` — the rebrand replaced Langflow with "Hanzo Flow" and the
space landed inside the identifier. esbuild rejects the config before Vite
starts, so the whole frontend stage dies with
`Expected ")" but found "Flow"`.
Usages now match the existing declaration. Swept the tree: this was the only
file the sed corrupted.
Co-authored-by: Hanzo Dev <[email protected]>
With the phantom base uv.lock removed the build reached uv sync and failed:
error: Failed to determine installation plan
Caused by: Distribution not found at: file:///app/src/sdk
[tool.uv.workspace] lists four members — src/backend/base, ., src/lfx and
src/sdk — and the Dockerfile copied manifests for the first three only. uv needs
every member's pyproject.toml present when it resolves, even though the packages
themselves are installed later with --no-install-project.
The build failed with
failed to calculate checksum of ref ...: "/src/backend/base/uv.lock": not found
A uv workspace has exactly ONE lockfile, at the root, which line 54 already
copies. Members do not carry their own, and src/backend/base contains only
README.md, flow/ and pyproject.toml. The comment asserting the base member
required its own lock was wrong.
Verified against [tool.uv.workspace] members = [src/backend/base, ., src/lfx,
src/sdk] and the actual repo tree, rather than repointing the path at
src/lfx/uv.lock, which would have fed the wrong member's lock to the build.
Superseded: the Hanzo GitHub App pushes a webhook, so the forge tracks GitHub
without a per-repo workflow. This file called git.hanzo.ai/api/v1/.../mirror-sync
— a Gitea API for a system we no longer drive — and would sit inert in every repo.
One mechanism, in one place, instead of ~350 copies of a cron.
Empty commit to exercise the rewritten .hanzo/workflows/deploy.yml on
git.hanzo.ai. workflow_dispatch is accepted (HTTP 204) but creates no run on
this forge — every run is ev=push — so a real push is the only reliable
trigger. The previous failures were push events that predated the rewrite
reaching the mirror.
Co-authored-by: Hanzo Dev <[email protected]>
The previous pipeline called buildctl-daemonless.sh, absent from the job image
(catthehacker/ubuntu:act-24.04 per GIT_RUNNER_LABELS) — exit 127 — and read
secrets.GIT_CLONE_TOKEN, which exists on neither the repo nor the hanzoai org.
It had never produced an image.
Its kubectl step failed the same way and was futile regardless: cd.hanzo.ai
selfHeal restores the App CR from the reviewed pin in hanzoai/universe within
minutes, so the deploy step is removed rather than repaired.
This is now urgent rather than cosmetic: actions-runner-controller was
decommissioned 2026-07-27 and every GitHub workflow pinning an ARC pool was
removed, so this pipeline is the ONLY CI this repo has.
Replaced with the shape proven on hanzoai/playground and 14 siblings:
setup-buildx + login-action + build-push-action using GHCR_USER/GHCR_TOKEN/
GH_PAT, which DO exist as org secrets, plus an imagetools check because
build-push-action can exit 0 before the manifest resolves.
git.hanzo.ai mirrors this repo by PULL on a ~10-minute interval, and arcd runs
CI/CD there — so every push waited out that interval before anything built.
This asks Gitea to pull HEAD immediately.
Latency only: the repo already mirrors via the App webhook, so a missing
HANZO_GIT_TOKEN or a failed curl is non-fatal and never fails the push.
Idempotent (mirror-sync just pulls HEAD) and concurrency-coalesced.
These jobs asked for `hanzo-linux-amd64`. No runner advertises that label, so
they never ran — and crucially they did not FAIL either. A job requesting an
unadvertised label queues until the 24h timeout, which reads as "CI is slow"
rather than "CI is misconfigured", so this hid rather than surfaced.
Verified live against git.hanzo.ai /api/v1/admin/actions/runners rather than
inferred: the four ONLINE runners (git-runner-0..3) advertise exactly
ubuntu-latest, ubuntu-22.04, ubuntu-24.04 and hanzo-build-linux-amd64. The
three self-hosted hosts that carry other labels (spark gb10/cuda, dbc
m4max/metal, evo gfx1151/rocm) are all OFFLINE, and none of them advertises
a linux-amd64 pool anyway.
So there is exactly ONE usable pool today and this points at it.
Fleet-wide this was 27 jobs across 14 files in 8 repos. The one remaining
mention of the old label is a COMMENT in iam2/image.yml that documents this
very defect — deliberately left intact.