Skip to content

Persist workflows locally - #33

Open
gretzke wants to merge 5 commits into
mainfrom
feat/local-workflows
Open

Persist workflows locally#33
gretzke wants to merge 5 commits into
mainfrom
feat/local-workflows

Conversation

@gretzke

@gretzke gretzke commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Workflows can now be saved outside git repos, in a per-profile local storage directory managed by core (profiles//workflows/local/.json).

How it works:

  • The promote dialog gets a save location toggle: Repository (unchanged flow) or Local (no repo needed, just a name).
  • Local workflows are live. Reads always return the current file, there is no install step and no doc-hash update checking. Saving from the editor writes in place with optimistic concurrency (409 on concurrent modification).
  • The workflows page lists local workflows in their own labeled section. They can be edited, run, and promoted to a repository later (promotion accepts a local workflow as source).
  • Running a local workflow sends local: true on the run request, core reads the profile-local file and skips the install-sync gate while keeping hook and resolution validation.

Closes #20.

constructor(deps?: Partial<LocalWorkflowStoreDeps>) {
this.deps = {
fileSystem: deps?.fileSystem ?? FileSystem.getInstance(),
devMode: deps?.devMode ?? (() => process.env.NODE_ENV === 'development'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:
BACKEND-ONLY RULE. process.env.NODE_ENV is NOT reliably set in Uniswap
backend Node.js ECS containers. Pulumi's UniServiceConfig injects
ENV=<stackName> (dev/staging/prod), not NODE_ENV. The runtime Dockerfile
(containers/Dockerfile.ec2) does not set NODE_ENV either.

Comparing NODE_ENV to 'production' or 'development' for security-relevant
decisions (origin allowlists, auth bypasses, rate-limit tuning, feature
flags) falls through to the dev/fallback branch in prod.

Real incident: Cantina finding #669 — WebAuthn origin allowlist in
privy-embedded-wallet used NODE_ENV || 'development', making
*.vercel.app + localhost permanently allowed origins in prod. Enabled
attacker-controlled rp.id in passkey registration, persistent wallet hijack.

Prefer process.env.ENV === 'prod' (see
packages/services/compliancev2/src/dependencies.ts:71) or the dual-read
pattern process.env.ENV || process.env.NODE_ENV || 'local' (see
packages/services/platform-service/src/init.ts:107).

This rule does NOT apply to frontend/universe code. Bundlers (Vite,
Webpack, Metro, WXT, Next.js) statically replace process.env.NODE_ENV at
build time, so the value IS reliable there. Configure Semgrep Cloud to
apply this rule only to the Uniswap backend repo, and keep the path
exclusions below as a second line of defense.

The benign pattern process.env.NODE_ENV !== 'test' for guarding server
auto-start is deliberately NOT matched.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
devMode: deps?.devMode ?? (() => process.env.NODE_ENV === 'development'),
devMode: deps?.devMode ?? (() => {
const env = process.env.ENV || process.env.NODE_ENV || 'local';
return env === 'dev';
}),
View step-by-step instructions
  1. Replace the NODE_ENV check in the default devMode implementation with the backend-supported environment variable.
    Change () => process.env.NODE_ENV === 'development' to () => process.env.ENV === 'dev'.

  2. Keep the fallback only on the injected dependency, not on NODE_ENV.
    The constructor should continue to prefer deps?.devMode first, and only use the ENV-based default when no override is passed.

  3. If this code needs to work in more than one runtime that may still set NODE_ENV, use the approved dual-read pattern instead of reading NODE_ENV directly.
    Set the environment name with const env = process.env.ENV || process.env.NODE_ENV || 'local', then check env === 'dev'. This avoids falling into a development branch in production when NODE_ENV is unset.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by uniswap-backend-node-env-prod-check.

You can view more details about this finding in the Semgrep AppSec Platform.

@david-uniswap david-uniswap left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran the core workflow tests (87 pass) and the touched frontend suites (31 pass) locally, and read through the store, promotion service, and run-gating changes. The concurrency story holds up: keyed mutex plus hash check inside the write, atomic tmp+rename, and the promotion preview goes stale on doc-hash or profile change. The NUL sentinel for the local pseudo-repo is a nice trick and it's tested for non-collision. LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow persisting workflows locally instead of just in git repos

2 participants