Commit Graph
288 Commits
Author SHA1 Message Date
stroblme 245e68449f Memory-queue ids carry a per-instance boot token, so a restart without Redis no longer overwrites earlier run rows 2026-10-14 23:33:25 +02:00
stroblme 2ac997ce37 ValueSource kind includes panel; Panels dialog no longer suggests the screen port openpanel does not serve 2026-10-14 23:32:32 +02:00
stroblme d423c47ca6 Socket snapshot carries the nodes waiting for a machine, so a reconnecting browser restores their queued dots 2026-10-14 23:31:54 +02:00
stroblme 30fa2b07db Remote exception names are cut to 128 identifier characters before a class is built for them 2026-10-14 23:31:47 +02:00
stroblme df78cdac72 Artifact sweep keeps what a flow input's initial references, published or draft 2026-10-14 23:30:46 +02:00
stroblme 1813b5d722 Runs export: a truncated one counts the selection into X-Total-Count; the SDK and fluksio export warn how many runs were left out 2026-10-14 23:29:48 +02:00
stroblme 8c50c22444 Add websocket node: client or served socket, listener or sender
ws:// and wss:// URLs dial out and hold a supervised connection; a path is
served under /hooks/<flow>/ with an optional secret. Outputs only listen,
inputs send. Frames are JSON keyed by port, with json_key and on_connect.
2026-10-14 18:42:19 +02:00
Melvin Strobl 2008150692 Round dashboards: one face at a time, a face per widget type, paged by swipe, dots and keys; slider reading; face row in the editor 2026-10-14 15:24:25 +02:00
Melvin Strobl be3446cb16 Round dashboards: shape and ring, the ring drawn and turned in every look, Knob preset and face guide in the editor 2026-10-14 14:23:55 +02:00
Melvin Strobl 0264310d4f Defer ffmpeg in the image; record the decision 2026-10-14 07:33:59 +02:00
Melvin Strobl 1964c5b2e8 Clip transcoding: ffmpeg in the backend image, panel recipe in the node docs 2026-10-14 07:30:36 +02:00
Melvin Strobl 659799d1e5 Panel I/O: PanelDef.io ports, scoped publish and upload, I/O section in the Panels dialog 2026-10-13 21:10:54 +02:00
stroblme d482b32970 Do not check a pinned certificate's dates
A pin trusts one exact certificate, so an expired one or an engine clock
that is off no longer locks a device out (X509_V_FLAG_NO_CHECK_TIME).
2026-10-07 10:11:08 +02:00
stroblme cd6f8fec55 Lift certificate pinning into the connector contract
fluksio.flow.connector_tls: trust the certificate a device presented on
first contact, kept in ConnectorNode.store, and name the new fingerprint
when another one is refused. Additive, so no contract version bump; hue
and wfrac use it.
2026-10-07 09:48:24 +02:00
stroblme e9c3d0944d Portal link: send the instance credential as Authorization: Bearer
Not ?token= in the tunnel URL, which access logs keep. The hub has to be
upgraded first; websockets>=14 for additional_headers on the asyncio client.
2026-10-07 09:41:44 +02:00
stroblme ff3a31e515 Hand a provisioned worker its token in the environment, not argv
The generated batch script exported nothing and passed `--token`, which
any user on a shared compute node reads from `ps`. It now exports
FLUKSIO_WORKER_TOKEN after `prerun`, which the worker already reads.
2026-10-07 09:32:01 +02:00
stroblme ed8aee2387 Authenticate sockets by header or one-time ticket instead of ?token= 2026-10-07 08:58:22 +02:00
stroblme 094fed7cea Serve artifacts of a type that can run script with CSP sandbox and nosniff 2026-10-07 08:43:29 +02:00
stroblme 953a983919 Bind a worker's name and labels into its token; list and revoke tokens by jti 2026-10-07 08:42:14 +02:00
stroblme 9ef7f90dd8 Keep query-string tokens out of the uvicorn, nginx and Traefik access logs 2026-10-06 17:32:51 +02:00
stroblme 0a74845a7f Quote names put into SDK client URL paths; share the helper with MCP 2026-10-06 17:23:49 +02:00
stroblme 243d39b591 Give connectors a store that survives restarts, and a way to clear it
`ConnectorNode.store` keeps short strings per connector package in the
engine's database (`connector_state`, migration f8c3e5a91d27), so the nodes of
one package share them and `fluksio serve` needs nothing new: get, set and
delete, at most 64 kB a value and 256 keys a package. A superuser lists the
keys (never the values) and clears them through `/connectors/{node_type}/state`,
or under Stored state in a connector node's panel. Documented in the connector
contract.
2026-10-06 17:19:48 +02:00
stroblme 3758415f21 Enrol over http only to loopback; serve the API docs to this machine
The enrolment exemption covered any *.local host, which mDNS answers with
whoever claims it; it is now localhost, 127.0.0.0/8 and ::1. /docs, /redoc
and openapi.json stay closed in production and otherwise answer only a
loopback caller unless API_DOCS_ENABLED is set, which the dev compose does,
so `fluksio serve --host 0.0.0.0` no longer hands the schema to the network.
2026-10-06 12:20:13 +02:00
stroblme db045770e8 Harden the remote worker: TLS off loopback, full digests, filtered env
fluksio-worker now refuses ws:// and http:// engine and artifact URLs unless
they are loopback or --insecure is given; the engine still accepts both.
An artifact reference must be sha256: plus 64 hex digits before it is joined
into a path, and node code gets the host environment through the engine's
credential denylist instead of all of it.
2026-10-06 12:18:30 +02:00
stroblme ae290b5421 Quote every path parameter the MCP tools put into a URL
httpx resolves `..` before sending, so save_flow("../ai/config") reached
PUT /ai/config. Each segment is now percent-encoded, dots included.
2026-10-06 12:16:48 +02:00
stroblme c5a8f170da Count per-address limits on the hop a trusted proxy added; limit signup
The limits read the leftmost X-Forwarded-For entry, which the client writes,
and the LAN nginx appended to it. The address now comes from uvicorn's
proxy-headers middleware (rightmost hop not in FORWARDED_ALLOW_IPS), applied
in the app too so tunnel-replayed calls resolve the same way; nginx sets the
header to $remote_addr and the compose backend trusts the Docker networks.
/users/signup gets a per-address limit of 10 an hour.
2026-10-06 12:15:58 +02:00
stroblme a168bcbdee Refuse names that are not names at the store boundary
DELETE /flows/%2e%2e reached FlowStore.delete_flow(".."), which removed
the data directory, and /flows/_lib removed every shared node. Every
path the flow and dashboard stores build from a flow, node, library or
dashboard name now goes through checked(), which raises InvalidName
(400) for anything _validate_name refuses. _validate_name uses
fullmatch, so a trailing newline is refused too.
2026-10-06 11:59:25 +02:00
stroblme 6802cd2203 Make changing engine-run code superuser-only
Writing node source, installing modules and any flow save that adds,
retypes or repoints a code node (Function, Command), edits a Command
node's settings or changes a node's resources.env now answer 403 for
everyone but a superuser. MCP tools and the assistant reach the same
routes, so they inherit the gate. Node types report runs_code.

The editor shows code read-only with a note, disables a Command node's
settings, leaves code nodes and reusable nodes out of the palette and
paste, and the Modules page offers no install.
2026-10-06 11:36:30 +02:00
stroblme 2914d860b1 Make self-service signup opt-in via SIGNUP_ENABLED
/users/signup answers 403 unless SIGNUP_ENABLED is set. A public
GET /utils/config/ tells the login page, which hides the sign-up link,
and the signup page explains that an administrator creates accounts.
2026-10-06 11:24:27 +02:00
stroblme a057b61859 Explain the terminal chart colours by the terminal theme
The docstring still justified the ANSI colours by the old one-hue web ramp.
The web now draws from the categorical --chart-1…8; the terminal keeps named
ANSI colours so it follows the user's terminal theme.
2026-10-06 07:24:54 +02:00
stroblme afe0c94a24 Talk to wren nodes from the chat's Control tab, opened from Home 2026-10-05 20:59:11 +02:00
stroblme 14d3208a5d Add the wren node: a typed command into values for the devices wired to it 2026-10-05 20:58:59 +02:00
stroblme a677409079 Find a flow's declared inputs and outputs in the global search
/search lists each input and batch output under its qualified message,
and picking one opens the flow on the label's id, which the editor now
treats like a click on the label: it opens the flow's settings.
2026-10-05 18:15:35 +02:00
stroblme 901e85f3fb Word the soak harness's host-safety note host-neutrally 2026-10-05 18:01:34 +02:00
stroblme f14f7bd413 Make narrowed backend test runs and the quarantine test order- and load-proof
The session db fixture now disposes the engine before deleting the file, since a
module under a no-op db (tests/sdk, tests/flow) may have connected first; the
sdk import tests forget the study packages they import; the supervision tests
keep the real quarantine length so it cannot expire mid-assertion.
2026-10-05 16:54:00 +02:00
stroblme 929933d7c3 Record which inputs a run's caller passed, and mark defaults from it
`Run.passed` (migration d5a3c8e1f246) keeps the names the caller chose, so
the run detail and the comparison table can mark a value as the flow's own
again; rows from before it fall back to the names their params lack. Retries
hand on their parent's list, imports record their entry's.

Also: a cancel of a queued run no longer leaves its id in `_cancelled`, a
linked comparison no longer crashes before the list loads, and the pre-commit
failures are fixed (stale client, `text-[10px]`, worker formatting).
2026-10-05 15:01:33 +02:00
stroblme bca28dbf75 Keep a run's lease while its pipeline builds; allow cancelling it then
A claimed run only joined the leased set once its pipeline was built, so a
build waiting for a busy worker pool got the run swept as abandoned. Cancel
now also stops such a run, and a terminal status being overwritten is logged.
2026-10-05 11:23:54 +02:00
stroblme 3f818c878c Back to development 2026-10-03 11:55:17 +02:00
stroblme e5a9a3738d Release v0.1.6
Publish / publish (push) Failing after 39s
2026-10-03 11:51:21 +02:00
stroblme 0649062327 Bound warm workers' host memory and account local RAM
A worker over FLOW_WORKER_MAX_RSS_MB (--worker-max-rss) when its node
returns is retired instead of kept warm, in the shared pool and in every
declared one; 0, the default, keeps them all. The engine now accounts its
own memory (FLOW_RAM / --ram, else the box or the cgroup limit less 1 GB),
so a node's `ram` declaration holds locally. A declared environment the
shared pool already has is that pool rather than a duplicate set of warm
workers.
2026-09-27 16:11:13 +02:00
stroblme 7240b7b823 Add the assistant: a chat that drives the MCP tools in-process
A provider set under Settings (OpenAI-compatible; key encrypted in
flow-data/ai.json) turns on an Assistant panel in the sidebar. Each turn runs
the FastMCP tools in-process as the person chatting, streams the rounds over
SSE and keeps one transcript per user. The panel floats on any screen and
shares the editor's lane: a settings panel keeps the top half, the chat the
bottom. The MCP client is now built once, for the endpoint and the chat.
2026-09-16 23:28:23 +02:00
stroblme af18d134a4 Add POST /runs/import for runs executed elsewhere
A history recorded before the move (MLflow experiments) had nowhere to
go, since runs were only made by running a flow. Each entry is stored as
it ended: parameters completed and checked like a submit, cause
"import", empty commit and code digest, metrics and artifacts written
as given, nothing queued or announced. Run.external_id (new migration)
makes a second import a no-op and is an export column so a row joins
back. The request is all or nothing and capped at 200 entries;
Client.import_runs pages a longer history.
2026-09-16 17:01:07 +02:00
stroblme ff4feb7af6 Drop an unused fixture from the ignore-walk test 2026-09-16 17:01:07 +02:00
stroblme 831bda9c46 Sync only the declaring module before a run; skip git-ignored paths
`fluksio run` walked the working directory and imported every module in
it, which ran a research repository's analysis scripts before the run was
submitted. A flow's origin now records the file and module name that
declared it, and run/sweep import just that, under the same name so the
generated node bodies do not change. A flow with no record still walks,
now skipping what .gitignore covers.

Every code-defined flow reports `flow` changed once on its next sync,
since the origin gained two fields.
2026-09-16 16:55:02 +02:00
stroblme f950cfbf6c Bind serve's socket before writing client.json
A serve that lost the port race had already signed in, so client.json
named a port another engine held and every client talked to that one.
The socket is now bound first and handed to uvicorn.
2026-09-16 16:47:11 +02:00
stroblme 42ea2e50ac Export FLOWS and build from the top-level fluksio package
A test of the declarations had to import them from fluksio.sdk while the
guide teaches `from fluksio import ...`. The worker's inert module gets the
same two names so a module importing them still loads inside a node.
2026-09-16 16:44:47 +02:00
stroblme 02907bbe27 Leave a dotted key out of the runs export header
_leaves named a column for a key like `-1.0` that _dig can never read
back, so the export showed an always-empty column. Skip such a key.
2026-09-16 16:43:52 +02:00
stroblme d4f0345e97 Sweep stale e2e fixtures before a run, not after one
An aborted Playwright run never reaches `afterAll`, so its flows and
dashboards stayed in the store until somebody deleted them by hand. A
setup-project test now removes them before the next run, which is the
only point that is reached even when the run before it died.

The suite runs against whatever stack is up — somebody's own instance
with real flows in it — so the predicate matches only the shape every
fixture has, `test_<spec>_<base36 Date.now()>` with an optional suffix,
and only once that timestamp is an hour old.

Also correct the coverage-html fallback hint: `make clean` cannot clear
a root-owned htmlcov either, for the same reason `coverage html` cannot
write it.
2026-09-14 16:27:36 +02:00
stroblme 4faf5312ff Measure the bar where it settles, not mid-spring
`widgets.spec.ts` took a one-shot `boundingBox()` of a fill that is still
moving. Two ways that reads wrong. "a bar draws a row per reading" compared
two widths that are both zero until the socket has delivered the readings,
and a plain `expect` over a captured number does not retry. "a reading over
its scale fills the track and no more" allowed the fill one pixel past the
track, but the house spring is underdamped: `motion`'s own generator puts the
fluksio look at 100.66% of the track 276ms in (material 100.91%, glass
103.58%), which on this tile's ~240px track is ~1.6px — over the tolerance,
and reached only if the sample happens to land near the peak.

Neither is the widget. `fractionOf` clamps the reading to [0, 1] and
`.dui-bar-track` is `overflow: hidden`, so nothing past a full track is ever
drawn; a bounding box just is not clipped by an ancestor's overflow. So both
now poll: one until the shorter reading is drawn shorter, the other until the
fill is flush with its track — which is a stricter reading of "and no more"
than the pixel of slack was.

The live-socket test gains the other half of what that spin cost: an ASGI
task that never returns is one uvicorn's graceful shutdown waits on forever,
which is why `fluksio serve` could only be SIGKILLed and left `serve.pid`
behind.
2026-09-14 15:02:42 +02:00
stroblme 0f040b6e1d Merge branch 'main' of git.stroblme.de:Fluksio/app
# Conflicts:
#	frontend/src/client/sdk.gen.ts
2026-09-07 08:41:45 +02:00