Commit Graph

22669 Commits

Author SHA1 Message Date
Yuliia Pominchuk
88e6a4cd15
docs: Say the identity section plainly
The previous pass was shorter but not clearer: it opened by announcing its own
importance, then leaned on "a run at 3am has nobody to ask" to carry the point.

Rewritten in the register the Storage section already uses — state the fact,
then the reason. The two cases are labelled rather than narrated, so the
structure is visible without prose holding it together.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 14:49:00 +02:00
Yuliia Pominchuk
5fb7c303bb
docs: Tighten the catalog's identity section
Same three ideas, roughly half the words: a live request proves who is asking, a
3am run has nobody to ask, and a minted token has to be re-checked because it
only proves who was named when it was issued.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 14:46:22 +02:00
Yuliia Pominchuk
42336c22e7
docs: Describe the workflow catalog
A feature description for people who did not follow the work: what the catalog
is for, the decision it rests on, how identity and scheduling actually work, and
where it is still thin.

Separate from the handoff notes, which are a working log. This one leads with
the problem — a workflow is shared, but the accounts it runs on are the
builder's, and a Schedule Trigger belongs to the workflow rather than to a
person.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 14:27:25 +02:00
Yuliia Pominchuk
7cbba6f149
fix(core): Offer a workflow that accepts any input, rather than none
A trigger set to "Accept all data" was excluded for declaring no contract. But
declaring nothing is not the same as being unofferable: the workflow simply
takes no input, which is exactly where a declared contract with no fields in it
lands — and that one was offered. So was a manual trigger, which carries less of
the builder's intent than any Execute Workflow Trigger does.

The line was in the wrong place. Three cases that all render an empty form and
start with `{}` now behave alike.

This also takes the pre-1.1 trigger with it: it has no mode to set, reads as
passthrough, and was excluded for the same wrong reason. A builder whose node
predates 1.1 sees no dropdown to fix and no explanation, so excluding it bought
nothing.

`own-schedule` and `no-start-node` remain, and both say something a builder can
act on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 14:08:05 +02:00
Yuliia Pominchuk
8670414dc8
fix(core): Show the catalog what was shared, not what may be administered
`findAllWhere` skips its per-user narrowing whenever the caller holds the scope
globally, so `findAllWorkflowsForUser(user, ['workflow:execute'])` returned every
`shared_workflow` row on the instance to anyone with global execute — an
instance owner, for one. Their catalog was every workflow there is, other
people's personal ones included, and the listing handed out those names.

Not a lie in RBAC terms — an owner may run any of them — but the catalog asks a
different question. It offers "the workflows you were given", and a global scope
is an administrative capability, not a grant of a particular workflow.

`findAllWorkflowsForUser` takes a `sharedWithUserOnly` option that keeps the
narrowing on regardless, and the catalog passes it. Default behaviour is
unchanged for every other caller. An owner still sees their own workflows —
membership of their personal project is what puts them there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:46:44 +02:00
Yuliia Pominchuk
1cf058e4fe
fix(editor): Give the catalog's schedules section room below it
Since the runs section went, the schedules are the last thing on the page and
the list sat on the page padding alone, which doesn't read as an end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:47:41 +02:00
Yuliia Pominchuk
a1f7afa579
fix(editor): Cut what the schedule form was asking twice
Two controls earned their removal.

"Saved as 0 0 9 * * *" put the storage format back in front of people the
picker exists to keep it away from — the same file's own comment says cron is
not the interface. The controls already read as the sentence they compose.

"Run on this schedule" was a question with one sensible answer: nobody sets up
a schedule switched off. Pausing is still worth having — it keeps the row and
its inputs while taking the scheduler job away — so it moves to the schedule's
row in the list, next to changing and removing it. Editing a paused schedule now
carries its paused state through rather than quietly starting it again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 10:26:57 +02:00
Yuliia Pominchuk
ce73861ba8
fix(core): Run a catalog workflow with the caller's own credentials
The catalog's run endpoint never attached a credential context. `execution-
context.ts` only picks one up when `encryptedRunnerIdentity` is set, and nothing
set it, so a dynamic credential had no end user to resolve to and the workflow
quietly ran on whatever it stores itself — the opposite of the point.

It affected every run from the catalog, not any particular trigger. The
scheduled path had identity from the day it was written, because that one mints
a token; the attended path, where the identity was sitting right there in the
request, had none. The design note claiming manual runs take identity from the
session described an intention, not the code.

Now the controller reads the session token off the request and the run service
encrypts it, the same way the editor's manual execution has always done. A
minted identity still wins when one is supplied, so the unattended path is
unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:25:54 +02:00
Yuliia Pominchuk
33c7abd5f4
fix(editor): Make the catalog badges say one thing each
"Takes input" and "No inputs" could appear on the same card, and both were
right: a trigger set to declare fields but with none added yet takes input in
principle and none in practice. It read as a bug. On every other card "Takes
input" just restated the count beside it.

The trigger badge is gone. What it uniquely carried — that a workflow can only
be run on demand, never scheduled — now shows as "On demand only", and only on
the cards where it is true. A badge should mark the exception, not restate the
default.

Copy checked against the content-design guidelines: sentence case, no trailing
period on a fragment, and the tooltip's two sentences both end in one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 14:52:57 +02:00
Yuliia Pominchuk
8d2e1ad7c2
fix(editor): Fix the catalog card chrome
Three problems, one of them a real bug.

The icon buttons carried `type="tertiary"`, but `N8nIconButton` takes `variant`
— `type` fell through to the DOM as an HTML attribute and the buttons rendered
in the default solid style. All three actions therefore looked equally primary,
and a disabled schedule button looked no different from an enabled one. They are
`variant="ghost"` now, so only Run reads as the primary action.

The footer put the badges and the buttons on one row. A card is ~280px and the
badges alone can fill it, so the buttons were pushed past the card's edge — and
because a grid item defaults to `min-width: auto`, a nowrap badge wider than its
track dragged the whole card out of its column. The two now stack, and the card
may shrink.

The next-run badge showed seconds, which are always `00` for a schedule that
fires on the minute.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 14:36:04 +02:00
Yuliia Pominchuk
f0b71fd1f3
feat(core): Require a declared trigger before a workflow can be scheduled
Running a workflow now and putting it on a schedule were held to the same bar,
and they should not be. Running once is bounded — the person is there, it
happens once, they see the result — and anyone with execute access could have
run it from the editor anyway, so a manual trigger is enough to justify it. A
schedule is unattended, recurring, and acts with that person's accounts for as
long as it stands.

That is too large a commitment to infer from the mere presence of a start node.
The Execute Workflow Trigger is the builder saying "this is meant to be called
by something other than me"; a manual trigger says the opposite. Subscriptions
now require the former, on create and on update — the trigger can be taken back
out after someone subscribed, and changing a schedule must not quietly re-bless
it.

Manual-trigger workflows stay in the catalog and stay runnable. Their schedule
button is disabled with the reason on it rather than hidden, so nobody is left
looking for an option that silently vanished.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 14:18:19 +02:00
Yuliia Pominchuk
af797b24d1
refactor(core): Remove the catalog runs endpoint
Nothing calls it: the catalog now sends people to the workflow's own execution
list, which shows the same runs with their data instead of a name, a time and a
status. Takes `CatalogService.listRuns`, its limit and its response types with
it, and drops the `ExecutionService` dependency that existed only for it.

The `catalogRunUserId` metadata the run service writes stays. It is the only
record of which person a run belongs to — a scheduled run has no session behind
it, so nothing else on the execution says whose it is — and unlike a query, it
cannot be reconstructed once it is not written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:10:24 +02:00
Yuliia Pominchuk
4923503526
feat(editor): Send the catalog to a workflow's own execution list
The "Your runs" section was a flat list of workflow name, time and status —
which is strictly less than the executions page already shows for the same runs,
and mixed several workflows into one stream nobody was reading as a whole. Each
card now links to that workflow's executions instead, where the run and its data
are.

Removes the store, api and type plumbing behind the section. The
`GET /catalog/runs` endpoint it read is left in place but now has no caller.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:05:38 +02:00
Yuliia Pominchuk
926b1ddc3b
feat(editor): Page the catalog and show each person their own schedule
The card said how many schedules existed for a workflow, which is a number
nobody browsing needs — the listing behind it was already scoped to the
requesting person, so it was their own count, but a count answers the wrong
question. It now says when the workflow next runs *for them*, or that their
schedule is paused, and nothing at all when they have not taken it on.

The calendar button follows: it edits their schedule when they hold one, adds
another when they hold several, and creates the first when they hold none. The
tooltip says which, so the meaning never changes silently.

The grid pages at twelve. Paging happens in the browser rather than through the
API: the listing is already capped server-side and says so when it truncates, so
the whole set is in hand and paging it is presentation, not fetching.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:54:14 +02:00
Yuliia Pominchuk
6b4e60e942
feat(editor): Add the catalog to the sidebar
The route existed but nothing linked to it, so the tab could only be reached by
typing the URL. Sits under Personal, where someone looks for their own things
rather than a project's.

Gated on `workflow:list`, the scope the catalog endpoints require, so the entry
is not offered to someone whose first click would be a permission error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:43:09 +02:00
Yuliia Pominchuk
032f914a28
docs: Add the workflow catalog handoff
Records the architectural decision the feature rests on — one workflow with an
Execute Workflow Trigger, not a generated wrapper per subscriber — the reasons
the wrapper approach was rejected, and what a live walkthrough confirmed and
found. Open decisions that need the scheduler owners are called out, as is the
missing offline OAuth refresh that blocks shipping schedules.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:19:19 +02:00
Yuliia Pominchuk
56ac52b7a4
feat(editor): Let a person set up their own schedule for a catalog workflow
Cron is the storage format, not the interface. The people this is for pick a
frequency and a time; one pair of functions is the only place the two
representations meet, so a schedule survives a round trip and comes back as the
same choice in the form. An expression the picker cannot express reads back as
the default rather than as a wrong-but-plausible choice.

The run form moves into its own dialog alongside the schedule one, so the grid no
longer shifts when a workflow is selected, and a card whose workflow declares no
fields runs outright instead of opening an empty form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:18:32 +02:00
Yuliia Pominchuk
7afb35df89
feat(core): Run a catalog workflow on a person's own schedule
A subscription is a cron expression, a timezone and a set of inputs, provisioned
as one durable job owned by the subscription rather than by any node. Creating
one records consent in the same transaction: a schedule is the only reason the
grant exists, so asking twice would be asking the same question twice.

Two stores have to agree — the rows here and the jobs the scheduler runs from —
and nothing keeps them in step, so every path deprovisions before it deletes and
treats a failed provision as a reason to put the row back rather than leave the
two disagreeing.

The handler proves again everything a live request would have proved: that the
person still exists and is enabled, still consents, and may still execute the
workflow. None of those is an error when it fails — consent gets withdrawn and
access gets removed in the ordinary course of things — so the occurrence reports
no dispatch. It loads the subscriber with their role, since there is no
authenticated request to take a fully-loaded user from and the access check reads
the scopes it carries.

Identity is minted per occurrence rather than stored: the token is short-lived by
design, and one written at subscribe time would be long expired by the first run.
The occurrence's dedup key means a redelivered task recognises the execution a
previous delivery already created.

`RunnerBindingService` now reads the binding row instead of an in-memory map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:15:27 +02:00
Yuliia Pominchuk
7237095654
feat(core): Store catalog consent and personal schedules
Two tables behind running a catalog workflow on your own schedule. They are
split because the keys differ: consent is one grant per (workflow, person),
while one person may want several schedules for the same workflow. The
subscription hangs off the grant by composite foreign key, so withdrawing
consent takes the schedules with it.

The scheduler side needed widening to hold them. A job was identified by
`(workflowId, nodeId)`, which is what provisioning diffs and deprovisioning
deletes by, and a per-person schedule has no node — several people hold their
own schedules for the same workflow. `scheduled_job` gains a nullable, opaque
`ownerId` that the scheduler only ever pairs with `taskType` and never
interprets, so a feature can own jobs without the scheduler learning about the
feature.

An owner-scoped job deliberately records no `workflowId`: that column is a
foreign key onto `workflow_published_version`, so it means "belongs to a
published trigger", which these do not. The type makes passing one impossible.
It also means nothing in the database removes these jobs when their owner goes,
so callers must deprovision explicitly.

The column is added with a raw `ALTER TABLE`: on SQLite the schema builder
rebuilds the table, and `scheduled_task` cascades from it, so the rebuild would
delete every queued occurrence on the instance.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:14:48 +02:00
Yuliia Pominchuk
19a830363c
fix styles 2026-08-05 10:57:04 +02:00
Yuliia Pominchuk
d400001ec6
feat(editor): Add a catalog tab for running workflows
A person who was granted execute but not read now has somewhere to go: a list
of what they may run, a form built from each workflow's declared input contract,
and their own run history. No canvas, no node graph.

The form is rebuilt on every selection rather than reused, so values do not
carry over from the workflow looked at before.

A capped listing is shown as capped. The backend reports when it cut the list,
and hiding that would leave someone hunting for a workflow that was simply not
rendered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 21:11:39 +02:00
Yuliia Pominchuk
32fb2405e5
feat(core): Show a person their own catalog runs
Adds GET /catalog/runs, selected by the marker the run service writes rather
than by workflow, so someone sharing a workflow with colleagues sees only what
they started themselves.

Each entry is projected down from the execution summary. The summary also
carries lastNodeExecuted, per-node statuses and error details, which would hand
node names to someone who was given execute but not read.

Moves the route prefix to /catalog so runs are not addressed as a sub-resource
of workflows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 20:07:55 +02:00
Yuliia Pominchuk
1c7cde1ae9
feat(core): List the workflows a person may run
Adds GET /catalog/workflows, returning each workflow's name, description and
declared input contract — never its nodes or connections. Execute access must
not become a way to obtain the graph.

Workflows with no readable contract are left out rather than shown as broken:
the person browsing cannot fix them, and the builder is the one who needs to
know.

Describing a workflow means reading its trigger through a node context, so the
listing is capped. The cap is reported back as `truncated` and logged, because a
short list that reads as a complete one is worse than an obvious limit.

The finder returns one row per share path, so entries are deduped by id.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 19:56:20 +02:00
Yuliia Pominchuk
f78b151d7f
feat(core): Run a workflow from the catalog without opening the editor
Adds POST /catalog/workflows/:id/run for people who were granted execute but
not read: the endpoint never returns or requires the graph, and @ProjectScope
rejects before the handler.

The run is a production one even though a person pressed the button. A manual
run would honour a workflow-level "don't save manual executions" and silently
erase their history, and would feed them the builder's pinned test data instead
of a real result.

Values not named by the workflow's declared contract are dropped rather than
passed through, and the execution is tagged with the person who ran it so "my
runs" is a filter over existing execution storage rather than a new column.

Start-node precedence is extracted from findWorkflowStart rather than repeated,
so a catalog run enters a workflow through the same node a sub-workflow call
would.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 16:26:53 +02:00
Yuliia Pominchuk
0a36cf18a7
feat(core): Read a workflow's declared input contract outside its node
The fields a workflow declares on its Execute Workflow Trigger are what a run
surface renders a form from, so a caller needs them without executing the node.
Adds a service that answers eligibility and schema together — a workflow with no
readable contract cannot be offered for direct running, and the caller should
not have to ask twice.

Moves the extraction from nodes-base into n8n-workflow. It now has several
readers — the parent editor's resource mapper, MCP deriving tool arguments, and
the run surfaces — and they must all see the same fields. nodes-base re-exports
so call sites are unchanged.

A pre-1.1 trigger gates its input-source parameter behind the node version and
therefore declares nothing, which reads as passthrough. That is correct, and
covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 16:18:42 +02:00
Yuliia Pominchuk
bb91a9163d
spike: resolve credentials for an unattended user-bound run
Adds the identity path a run needs when it starts for a user with no session
behind it: a minted, short-lived token names the user, and the authorization a
live session would have carried is re-established at resolution time.

Four checks replace what a session validated implicitly - token signature and
expiry, user still enabled, binding still active, and execute access still
granted. The last one is the only thing that catches a user removed from the
project after granting, since that never touches the binding.

Kept as a member of the existing metadata union rather than a separate
identifier: N8NCredentialResolver injects N8NIdentifier directly, and the three
existing sources already discriminate on metadata.source.

Binding storage is an in-memory stub for the spike. Nothing mints this token
yet, so the path is unreachable at runtime; the stub must be replaced by the
real binding row before any of this is proposed for merge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 16:02:46 +02:00
Declan Carroll
23f4cca822
build: Bump Node.js to 24.18.1 and 22.23.2 (#35516)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 14:15:22 +01:00
n8n-cat-bot[bot]
05ebebd8f5
chore: Bump @vitest/browser 4.1.9 → 4.1.10 (patch) to clear 1 Aikido finding (#35514)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 14:14:45 +01:00
Ricardo Espinoza
936cdaa046
refactor(core): Break folder and workflow service dependency cycle (#35225) 2026-08-04 12:56:58 +00:00
Kai
6c8197e379
feat(editor): Add Changes diff tab to workflow review detail pane (#35431) 2026-08-04 12:54:26 +00:00
Lorent Lempereur
40dac4b90a
test: Assert debug mode pinned data with a retrying check (#35512)
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (22.22.3) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.18.0) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Publish API Schema / Cloudflare Pages (push) Waiting to run
2026-08-04 11:24:28 +00:00
Rob Hough
38f069172c
feat(editor): Add ChatMessage to @n8n/design-system (#35461) 2026-08-04 11:07:45 +00:00
mfsiega
c8602ab767
feat(core): Implement step:ready handler in @n8n/engine (no-changelog) (#35184)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 10:52:15 +00:00
José Braulio González Valido
d57a81ac60
fix(ai-builder): Keep model-provider outages out of eval builder scores (no-changelog) (#35293)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 10:52:11 +00:00
Kai
5e4a20d031
feat(core): Surface decision eligibility on the review detail (#35391)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 10:52:07 +00:00
Arvin A
8bdf69eb23
refactor(core): Tidy agent-eval runner and rating-persistence internals (no-changelog) (#35424) 2026-08-04 10:42:20 +00:00
n8n-cat-bot[bot]
1a2ea82626
chore: Persist Trivy scan results as a build artifact (#35506)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 10:41:45 +00:00
Iván Ovejero
f007542520
feat(core): Report database version in telemetry (no-changelog) (#35428) 2026-08-04 10:39:18 +00:00
Rob Hough
9f43b10959
chore(editor): Move Checkbox story to Core (no-changelog) (#35430) 2026-08-04 10:26:51 +00:00
Matsu
d077816742
ci: Ping release captain on new package hitting master (#35504) 2026-08-04 09:58:47 +00:00
Charlie Kolb
e41aae0fb6
ci: Show workflow_dispatch inputs in workflow run names (#35496)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 09:46:14 +00:00
Alexander Gekov
935cfb912e
feat(core): Gate n8n Connect with instance config (#35007)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 09:22:06 +00:00
Stephen Wright
03213e7466
fix(Kafka Trigger Node): Stop consumers reliably when a workflow is deactivated or updated (#35069)
Co-authored-by: Thanasis Gkliatis <thanasis.gkliatis@n8n.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 09:21:00 +00:00
Nour Alhadi Mahmoud
3b89fef4f4
feat(core): Add create-with-value variable policy for package import (no-changelog) (#34895)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 09:12:27 +00:00
Alex Grozav
cb0a806ee1
test: Repair vi.mock specifiers that no longer resolve (no-changelog) (#35491)
Co-authored-by: multica-agent <github@multica.ai>
2026-08-04 08:55:18 +00:00
oleg
120678011d
fix(editor): Lock the AI Assistant canvas while the agent is working (#35339) 2026-08-04 08:35:22 +00:00
Arvin A
d5845df4fc
fix(AI Agent Node): Stop pre-tool-call text leaking into agent responses (#35212) 2026-08-04 08:06:04 +00:00
Tomi Turtiainen
cdd6a737ff
test: Make Playwright notification waits assert instead of silently passing (#35325) 2026-08-04 08:03:05 +00:00
Tomi Turtiainen
e91497e92a
test: Deflake debug mode spec (#35203) 2026-08-04 08:02:01 +00:00
Sam Wooler
9c596228d9
docs(API): Generate OpenAPI docs from Zod DTOs and PublicApiController decorators (#34540)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 08:00:54 +00:00