@multiplier-labs/stepflow 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +146 -0
  2. package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +74 -0
  3. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
  4. package/.codekin/reports/comments/2026-04-11_comment-assessment.md +161 -0
  5. package/.codekin/reports/comments/2026-04-14_comment-assessment.md +34 -0
  6. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
  7. package/.codekin/reports/complexity/2026-04-11_complexity-report.md +152 -0
  8. package/.codekin/reports/complexity/2026-04-14_complexity-report.md +34 -0
  9. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
  10. package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +93 -0
  11. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
  12. package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +138 -0
  13. package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +121 -0
  14. package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
  15. package/.codekin/reports/security/2026-04-11_security-audit.md +297 -0
  16. package/.github/CODEOWNERS +15 -0
  17. package/.github/dependabot.yml +23 -0
  18. package/.github/workflows/ci.yml +38 -0
  19. package/.github/workflows/publish.yml +17 -5
  20. package/.github/workflows/security.yml +29 -0
  21. package/CHANGELOG.md +34 -0
  22. package/README.md +24 -0
  23. package/SECURITY.md +40 -0
  24. package/dist/{chunk-FQCT3I2A.js → chunk-5XJBAPXF.js} +527 -551
  25. package/dist/chunk-5XJBAPXF.js.map +1 -0
  26. package/dist/{chunk-U5QGN3RT.js → chunk-FMPJBK2N.js} +67 -55
  27. package/dist/chunk-FMPJBK2N.js.map +1 -0
  28. package/dist/{chunk-GXY66V7Q.js → chunk-JPXPB7U5.js} +52 -2
  29. package/dist/chunk-JPXPB7U5.js.map +1 -0
  30. package/dist/{chunk-XCF4PLN7.js → chunk-U3PCVK6O.js} +276 -61
  31. package/dist/chunk-U3PCVK6O.js.map +1 -0
  32. package/dist/events/index.d.ts +60 -7
  33. package/dist/events/index.js +1 -1
  34. package/dist/{index-CPfGsmrp.d.ts → index-Cd0IZ40L.d.ts} +56 -17
  35. package/dist/index.d.ts +15 -23
  36. package/dist/index.js +784 -547
  37. package/dist/index.js.map +1 -1
  38. package/dist/scheduler/index.d.ts +4 -4
  39. package/dist/scheduler/index.js +2 -2
  40. package/dist/storage/index.d.ts +322 -56
  41. package/dist/storage/index.js +4 -2
  42. package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
  43. package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
  44. package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
  45. package/docs/stepflow-api-reference.md +5 -4
  46. package/package.json +12 -8
  47. package/src/core/engine/concurrency.ts +229 -0
  48. package/src/core/engine/lifecycle.ts +176 -0
  49. package/src/core/engine.test.ts +536 -522
  50. package/src/core/engine.ts +116 -235
  51. package/src/core/index.ts +3 -3
  52. package/src/core/orchestrator/persistence.ts +165 -0
  53. package/src/core/orchestrator/plan-resolution.ts +140 -0
  54. package/src/core/orchestrator/state-transitions.ts +109 -0
  55. package/src/core/orchestrator/step-runner.ts +427 -0
  56. package/src/core/orchestrator.ts +57 -395
  57. package/src/core/types.ts +7 -29
  58. package/src/events/index.ts +4 -4
  59. package/src/events/memory.test.ts +39 -41
  60. package/src/events/memory.ts +7 -10
  61. package/src/events/socketio.test.ts +112 -162
  62. package/src/events/socketio.ts +20 -27
  63. package/src/events/types.ts +16 -19
  64. package/src/events/webhook.test.ts +489 -235
  65. package/src/events/webhook.ts +355 -139
  66. package/src/index.ts +19 -19
  67. package/src/planning/evaluate-condition.test.ts +153 -0
  68. package/src/planning/evaluate-condition.ts +105 -0
  69. package/src/planning/index.ts +6 -3
  70. package/src/planning/planner.ts +45 -152
  71. package/src/planning/planning.test.ts +303 -366
  72. package/src/planning/registry.test.ts +204 -251
  73. package/src/planning/registry.ts +20 -106
  74. package/src/planning/types.ts +23 -27
  75. package/src/scheduler/cron.test.ts +149 -150
  76. package/src/scheduler/cron.ts +58 -69
  77. package/src/scheduler/index.ts +4 -14
  78. package/src/scheduler/postgres-persistence.ts +89 -105
  79. package/src/scheduler/sqlite-persistence.test.ts +96 -96
  80. package/src/scheduler/sqlite-persistence.ts +40 -22
  81. package/src/scheduler/types.ts +4 -9
  82. package/src/storage/index.ts +5 -5
  83. package/src/storage/memory.test.ts +133 -183
  84. package/src/storage/memory.ts +21 -42
  85. package/src/storage/postgres-core.ts +550 -0
  86. package/src/storage/postgres-migration.test.ts +69 -0
  87. package/src/storage/postgres-transaction-pagination.test.ts +107 -0
  88. package/src/storage/postgres.ts +222 -937
  89. package/src/storage/sqlite-core.ts +151 -0
  90. package/src/storage/sqlite.test.ts +330 -281
  91. package/src/storage/sqlite.ts +225 -170
  92. package/src/storage/types.ts +33 -58
  93. package/src/utils/errors.test.ts +78 -80
  94. package/src/utils/errors.ts +27 -57
  95. package/src/utils/id.ts +3 -3
  96. package/src/utils/index.ts +6 -4
  97. package/src/utils/logger.test.ts +52 -58
  98. package/src/utils/logger.ts +4 -4
  99. package/src/utils/pinned-fetch.test.ts +84 -0
  100. package/src/utils/pinned-fetch.ts +118 -0
  101. package/src/utils/postgres-deps.ts +20 -4
  102. package/src/utils/retry.test.ts +47 -67
  103. package/src/utils/retry.ts +9 -9
  104. package/src/utils/safe-json.test.ts +166 -0
  105. package/src/utils/safe-json.ts +107 -0
  106. package/src/utils/webhook-signature.test.ts +111 -0
  107. package/src/utils/webhook-signature.ts +74 -0
  108. package/dist/chunk-FQCT3I2A.js.map +0 -1
  109. package/dist/chunk-GXY66V7Q.js.map +0 -1
  110. package/dist/chunk-U5QGN3RT.js.map +0 -1
  111. package/dist/chunk-XCF4PLN7.js.map +0 -1
  112. package/src/scheduler/postgres-persistence.test.ts +0 -906
@@ -0,0 +1,138 @@
1
+ # Documentation Audit: stepflow
2
+
3
+ **Date**: 2026-04-11T02:47:05.901Z
4
+ **Repository**: /srv/repos/stepflow
5
+ **Branch**: main
6
+ **Workflow Run**: 24ff56b6-9441-43f2-8563-c985920b774f
7
+ **Session**: ba7bd622-3564-476e-976b-21918677f818
8
+
9
+ ---
10
+
11
+ Now I have enough information to write the complete report.
12
+
13
+ ## Summary
14
+
15
+ **Audit date:** 2026-04-11 | **Repository:** `/srv/repos/stepflow` | **Branch:** `main`
16
+
17
+ The repository contains **3 documentation files** totalling **2,167 lines**. All three share the same commit timestamp (`2026-04-11 05:43:05`) from the initial import, so git-log staleness analysis is not applicable — all docs and code landed together. Cross-referencing documentation claims against the actual TypeScript source reveals the documentation is largely accurate and comprehensive, with **2 confirmed accuracy issues** and **1 minor fragmentation observation**.
18
+
19
+ **Health rating: Well-maintained** — minimal sprawl, no redundancy, nearly all claims verified against source.
20
+
21
+ ---
22
+
23
+ ## Documentation Inventory
24
+
25
+ | Path | Lines | Last Modified | Purpose | Status |
26
+ |------|-------|---------------|---------|--------|
27
+ | `README.md` | 408 | 2026-04-11 | Project overview, quick start, core concepts, feature tour, storage/event/scheduler options, planning system intro | Current |
28
+ | `docs/github-packages-setup.md` | 97 | 2026-04-11 | Publish and consume `@multiplier-labs/stepflow` via GitHub Packages; CI/CD token setup | Current |
29
+ | `docs/stepflow-api-reference.md` | 1,662 | 2026-04-11 | Full API reference: every class, interface, method signature, configuration option, PostgreSQL schema, integration patterns | Current (2 minor issues) |
30
+
31
+ ---
32
+
33
+ ## Staleness Findings
34
+
35
+ Because all documentation and source files share the same initial-commit timestamp, there is no temporal drift to report — no doc file predates its covered code or vice versa. No broken internal cross-links were found between the three files; the README's link to `docs/stepflow-api-reference.md` resolves correctly, and the GitHub Packages guide is self-contained.
36
+
37
+ No references to non-existent file paths, removed CLI commands, or deprecated config keys were found in any of the three documents.
38
+
39
+ ---
40
+
41
+ ## Accuracy Issues
42
+
43
+ ### Issue 1 — Non-existent `getSchedule()` method (confirmed)
44
+
45
+ **File:** `docs/stepflow-api-reference.md`
46
+ **Location:** Scheduler section (approximate line 499)
47
+ **Claim:** Documents a `scheduler.getSchedule(scheduleId: string): Promise<WorkflowSchedule>` method.
48
+ **Reality:** The `Scheduler` interface in `src/scheduler/types.ts` defines only `getSchedules(): Promise<WorkflowSchedule[]>` (plural, no `scheduleId` parameter). The singular `getSchedule(id)` does not exist on the interface.
49
+ **Impact:** Any developer who copies the shown usage will get a TypeScript compile error and runtime failure.
50
+
51
+ ### Issue 2 — `getInterruptedRuns()` method needs verification
52
+
53
+ **File:** `docs/stepflow-api-reference.md`
54
+ **Location:** Storage adapter section (approximate line 785)
55
+ **Claim:** References `storage.getInterruptedRuns()` on `SQLiteStorageAdapter`.
56
+ **Reality:** This method does not appear in the `StorageAdapter` interface (`src/storage/types.ts`) nor in the `WorkflowStorage` interface. It may be an undocumented implementation-only method, a leftover reference to a removed method, or a documentation invention. Requires reading `src/storage/sqlite.ts` to confirm.
57
+ **Impact:** Medium — if the method does not exist, documented usage will fail at compile time.
58
+
59
+ ### Everything Else — Verified Accurate
60
+
61
+ All of the following were cross-checked against source and match exactly:
62
+
63
+ - All exported names in `src/index.ts` match the TypeScript Types Reference section in the API reference.
64
+ - All `RunStatus`, `StepStatus`, `StepErrorStrategy` union literal values are correct.
65
+ - All `WorkflowContext`, `WorkflowStep`, `WorkflowHooks`, `WorkflowDefinition` interface properties and types are correct.
66
+ - All `StorageAdapter` and `WorkflowStorage` method signatures are correct.
67
+ - All 14 `BuiltInEventType` literals are correct.
68
+ - All `EventTransport` method signatures are correct.
69
+ - All `WorkflowSchedule` properties and `Scheduler` interface methods (except the `getSchedule` issue above) are correct.
70
+ - All 10 `ConditionOperator` values, all `Recipe`, `Plan`, `PlannedStep` interfaces are correct.
71
+ - All 9 error class names, error codes, and constructor signatures are correct.
72
+ - `WorkflowEngineConfig` and `StartRunOptions` interfaces are correct.
73
+ - `package.json` scripts (`build`, `dev`, `typecheck`, `test`, `test:watch`, `test:coverage`) match README claims.
74
+ - Peer dependency versions (`better-sqlite3 >=11.0.0`, `kysely >=0.27.0`, `pg >=8.13.0`) match `package.json`.
75
+ - Package name `@multiplier-labs/stepflow` and subpath exports (`/storage`, `/events`, `/scheduler`) are correct throughout all docs.
76
+
77
+ ---
78
+
79
+ ## Overlap & Redundancy
80
+
81
+ There is no meaningful overlap between the three files. Each occupies a distinct lane:
82
+
83
+ - `README.md` — conceptual overview and feature tour (prose-heavy, code snippets for motivation)
84
+ - `docs/github-packages-setup.md` — ops/distribution concern (PAT setup, CI/CD tokens, `npm version`)
85
+ - `docs/stepflow-api-reference.md` — exhaustive technical reference
86
+
87
+ Both `README.md` and `docs/stepflow-api-reference.md` contain a Quick Start section, but they use different examples (order-processing workflow vs. email-campaign workflow) and serve different readers (README = first-time evaluator, API ref = returning developer). This is a reasonable design choice, not a redundancy problem.
88
+
89
+ **No merge recommendations are warranted.**
90
+
91
+ ---
92
+
93
+ ## Fragmentation
94
+
95
+ With only 3 files, fragmentation is not a concern. The split between README and API reference follows widely accepted open-source convention. The GitHub Packages setup guide is appropriately separated as a distribution/ops concern that would clutter the main README.
96
+
97
+ No spec, proposal, or planning documents exist in the repo. No "completed feature" spec files need to be folded in or removed.
98
+
99
+ ---
100
+
101
+ ## Action Items
102
+
103
+ ### Delete
104
+
105
+ | File | Reason it's safe to delete |
106
+ |------|----------------------------|
107
+ | — | No deletion candidates. All three files serve distinct, active purposes. |
108
+
109
+ ### Consolidate
110
+
111
+ | Source Files | Target File | What to Keep / Drop |
112
+ |---|---|---|
113
+ | — | — | No consolidation needed. Files do not overlap in scope. |
114
+
115
+ ### Update
116
+
117
+ | File | Section Needing Update | What Changed |
118
+ |------|------------------------|--------------|
119
+ | `docs/stepflow-api-reference.md` | Scheduler section — `getSchedule(scheduleId)` method | Remove or replace with correct `getSchedules(): Promise<WorkflowSchedule[]>`. The documented method does not exist on the `Scheduler` interface. |
120
+ | `docs/stepflow-api-reference.md` | Storage section — `getInterruptedRuns()` on `SQLiteStorageAdapter` | Verify against `src/storage/sqlite.ts`. If the method no longer exists or was never part of the public interface, remove the reference. If it's an implementation detail not on the interface, add a note clarifying it is not part of the `StorageAdapter` contract. |
121
+
122
+ ---
123
+
124
+ ## Recommendations
125
+
126
+ 1. **Fix `getSchedule()` → `getSchedules()`** in `docs/stepflow-api-reference.md`. This is a confirmed broken API reference that will cause compile errors if followed. One-line fix.
127
+
128
+ 2. **Verify `getInterruptedRuns()`** by reading `src/storage/sqlite.ts`. If the method is absent, delete the reference. If present, ensure it is either promoted to the `StorageAdapter` interface (if intentionally public) or marked as an internal implementation detail.
129
+
130
+ 3. **Add a version changelog or `CHANGELOG.md`** if the package begins publishing new minor/major versions. Currently at `0.2.6`, with no changelog. As the API stabilises this becomes important for downstream consumers to understand what changed between releases.
131
+
132
+ 4. **Pin documented peer-dependency versions in the API reference.** The reference correctly lists minimum versions but does not mention tested/recommended ranges. As `better-sqlite3`, `kysely`, and `pg` release new majors, this becomes a source of drift. Consider a compatibility table or tested-with note.
133
+
134
+ 5. **Add a `CONTRIBUTING.md` or development-setup section** for contributors. The `package.json` has `dev`, `test`, `build`, and `typecheck` scripts, but there is no document explaining how to run the test suite locally, set up a Postgres instance for integration tests, or submit changes. This gap matters as the project gains external contributors.
135
+
136
+ 6. **Keep `docs/github-packages-setup.md` updated on PAT scope requirements.** GitHub periodically revises fine-grained PAT scopes. The current instructions reference `read:packages` — this is correct today, but is the most likely part of that document to go stale.
137
+
138
+ 7. **Consider lightweight doc-testing** (e.g., extracting TypeScript code blocks and running `tsc --noEmit` on them as part of CI). The one confirmed accuracy issue (`getSchedule`) would have been caught automatically if the Quick Start and integration examples were compiled as part of the build.
@@ -0,0 +1,121 @@
1
+ # Documentation Audit: stepflow
2
+
3
+ **Date**: 2026-04-13T01:17:34.517Z
4
+ **Repository**: /srv/repos/stepflow
5
+ **Branch**: main
6
+ **Workflow Run**: 54339665-09b6-4e19-8981-c8fc3144a296
7
+ **Session**: 386a6c34-ce83-4c68-a8e7-e668435ced26
8
+
9
+ ---
10
+
11
+ Now I'll produce the final structured report based on the audit findings.
12
+
13
+ ## Summary
14
+
15
+ **Total documentation files:** 3 (2,167 lines total)
16
+ **Files needing attention:** 1 (minor — one missing error class entry)
17
+ **Health rating:** Well-maintained
18
+
19
+ The repository has a lean, focused documentation set: one root README, one GitHub Packages setup guide, and one comprehensive API reference. All three files share the same last-modified date (2026-04-11, the initial commit), so there is no divergence between doc age and code age. Code examples, configuration keys, export paths, and API signatures were cross-checked against the source and are accurate. The only finding is a single missing entry in the error-class table.
20
+
21
+ ---
22
+
23
+ ## Documentation Inventory
24
+
25
+ | Path | Lines | Last Modified | Purpose | Status |
26
+ |------|-------|--------------|---------|--------|
27
+ | `README.md` | 408 | 2026-04-11 | Project overview, quick-start, install, basic usage examples | Current |
28
+ | `docs/github-packages-setup.md` | 97 | 2026-04-11 | Instructions for authenticating with and publishing to GitHub Packages NPM registry | Current |
29
+ | `docs/stepflow-api-reference.md` | 1,662 | 2026-04-11 | Complete API reference: WorkflowEngine, storage adapters, event transports, scheduler, planner, error types, TypeScript types | Current (1 omission) |
30
+
31
+ ---
32
+
33
+ ## Staleness Findings
34
+
35
+ No staleness issues detected. All three documentation files were committed in the initial commit (2026-04-11) alongside the source code they describe. There is only one commit in the repository (`bd16961 Initial commit`), so no code has changed after the docs were written.
36
+
37
+ No broken internal links were found:
38
+ - `README.md` references `docs/github-packages-setup.md` — file exists.
39
+ - `README.md` references `docs/stepflow-api-reference.md` — file exists.
40
+ - All markdown anchor links (`#error-classes`, `#workflow-engine`, etc.) resolve to sections present in their respective files.
41
+
42
+ ---
43
+
44
+ ## Accuracy Issues
45
+
46
+ **Issue 1 — Missing error class in API Reference**
47
+
48
+ `WaitForRunTimeoutError` is exported from `src/index.ts` and implemented in `src/utils/errors.ts`, but it does not appear in the error-class table in `docs/stepflow-api-reference.md` (the table covers eight error types; this is the ninth).
49
+
50
+ - **Exported symbol:** `WaitForRunTimeoutError` with code `WAIT_FOR_RUN_TIMEOUT`, constructor signature `(runId, timeoutMs)`.
51
+ - **Where it is thrown:** `WorkflowEngine.waitForRun()` when the timeout elapses before the run completes.
52
+ - **Impact:** Users who encounter this error and search the API reference will not find it documented.
53
+
54
+ **Issue 2 — Storage adapter naming ambiguity (very minor)**
55
+
56
+ The TypeScript types section mentions both `StorageAdapter` and `WorkflowStorage` interfaces without explaining that `WorkflowStorage` is the extended/current interface while `StorageAdapter` is the narrower legacy surface. This distinction is inferable from the source but is not stated in the docs.
57
+
58
+ No issues were found with:
59
+ - Install / setup instructions (match `package.json` scripts and peer deps).
60
+ - Subpath export paths (`/storage`, `/events`, `/scheduler`) — all match `package.json` `exports` field.
61
+ - Code examples for `WorkflowEngine`, storage adapters, event transports, scheduler, and planner.
62
+ - All nine documented config keys for `WorkflowEngineConfig`, `PostgresStorageConfig`, and `SQLiteStorageConfig`.
63
+
64
+ ---
65
+
66
+ ## Overlap & Redundancy
67
+
68
+ No meaningful overlap exists across the three files. Each file occupies a distinct niche:
69
+
70
+ - `README.md` — orientation and quick-start; deliberately high-level.
71
+ - `docs/github-packages-setup.md` — operational runbook for package publishing; not replicated elsewhere.
72
+ - `docs/stepflow-api-reference.md` — exhaustive API detail; README does not duplicate it.
73
+
74
+ The README links to the API reference rather than reproducing its content, which is the correct pattern.
75
+
76
+ **No merge or deletion actions are warranted on overlap grounds.**
77
+
78
+ ---
79
+
80
+ ## Fragmentation
81
+
82
+ No fragmentation detected. The documentation set is already consolidated: three files covering three distinct concerns. There are no orphaned spec documents, proposal files, or feature-design docs in the repository. The `docs/` directory contains exactly two files, both purposeful and non-overlapping.
83
+
84
+ No "plan" or "proposal" documents describing completed work were found.
85
+
86
+ ---
87
+
88
+ ## Action Items
89
+
90
+ ### Delete
91
+
92
+ | File | Reason |
93
+ |------|--------|
94
+ | *(none)* | No files are outdated, superseded, or describe completed proposals with no reference value. |
95
+
96
+ ### Consolidate
97
+
98
+ | Source Files | Target File | Notes |
99
+ |-------------|-------------|-------|
100
+ | *(none)* | — | No consolidation needed; the current three-file structure is appropriate. |
101
+
102
+ ### Update
103
+
104
+ | File | Section Needing Update | What Changed |
105
+ |------|----------------------|--------------|
106
+ | `docs/stepflow-api-reference.md` | Error Classes table | Add missing `WaitForRunTimeoutError` entry: code `WAIT_FOR_RUN_TIMEOUT`, constructor `(runId, timeoutMs)`, thrown by `waitForRun()` on timeout. |
107
+ | `docs/stepflow-api-reference.md` | TypeScript Types Reference | Add a one-sentence note clarifying that `WorkflowStorage` is the current extended interface and `StorageAdapter` is the narrower legacy one, so users know which to implement. |
108
+
109
+ ---
110
+
111
+ ## Recommendations
112
+
113
+ 1. **Add `WaitForRunTimeoutError` to the error-class table** in `docs/stepflow-api-reference.md`. It is exported, thrown at a documented public API boundary (`waitForRun`), and its absence is the only real gap in API coverage.
114
+
115
+ 2. **Clarify `StorageAdapter` vs `WorkflowStorage`** in the TypeScript types section — a single sentence noting which is the current recommended interface and which is the narrower legacy surface will prevent confusion for implementors.
116
+
117
+ 3. **Add a `CHANGELOG.md` or version-history section** to `README.md` as the project evolves past v0.2.6. The current single-commit history makes this unnecessary now, but without it, future contributors will have no changelog to update when they introduce breaking changes.
118
+
119
+ 4. **Protect documentation accuracy through CI.** Because the repository has no docs-linting step, future code changes could silently invalidate examples. Consider adding a `docs:check` script (e.g., `markdownlint`, `markdown-link-check`) to the GitHub Actions workflow so broken links and formatting regressions are caught automatically.
120
+
121
+ 5. **Keep the three-file structure.** The current organisation (`README.md` + `docs/github-packages-setup.md` + `docs/stepflow-api-reference.md`) is clean and proportional to the codebase size. Resist splitting the API reference into per-module files until the reference exceeds ~3,000 lines or the docs audience clearly segments by module.
@@ -0,0 +1,208 @@
1
+ # Security Audit: stepflow
2
+
3
+ **Date**: 2026-04-03T20:03:06.542Z
4
+ **Repository**: /srv/repos/stepflow
5
+ **Branch**: main
6
+ **Workflow Run**: 54523493-771d-44b8-b39f-03f2d6caa49e
7
+ **Session**: 53e402d9-8d47-49b4-9a27-03f2ae5ec912
8
+
9
+ ---
10
+
11
+ I now have all the information needed to produce the report. Here is the security audit:
12
+
13
+ ---
14
+
15
+ ## Summary
16
+
17
+ **Overall Risk Rating: Low**
18
+
19
+ The Stepflow project is a TypeScript workflow orchestration library with a mature security posture. No critical or high vulnerabilities were identified. The codebase demonstrates intentional, defence-in-depth practices: parameterized queries, SSRF protection with DNS-rebinding prevention, HMAC-SHA256 webhook signing, and delegated authorization. Recent commits (`48e5f0a`, `d61858c`) show that prior SSRF and SQL-injection issues were already discovered and remediated.
20
+
21
+ | Severity | Count |
22
+ |----------|-------|
23
+ | Critical | 0 |
24
+ | High | 0 |
25
+ | Medium | 2 |
26
+ | Low | 3 |
27
+ | Informational | 4 |
28
+
29
+ ---
30
+
31
+ ## Critical Findings
32
+
33
+ None.
34
+
35
+ ---
36
+
37
+ ## High Findings
38
+
39
+ None.
40
+
41
+ ---
42
+
43
+ ## Medium Findings
44
+
45
+ ### M1 — Missing Input Validation on `tableName` in `PostgresSchedulePersistence`
46
+
47
+ **File:** `src/scheduler/postgres-persistence.ts:153`
48
+
49
+ **Description:**
50
+ `PostgresSchedulePersistence` validates `schema` with a strict regex (`^[a-zA-Z_][a-zA-Z0-9_]{0,62}$`) but applies **no equivalent validation to `tableName`**. The raw `tableName` string is concatenated into `fullTableName` (`${this.schema}.${this.tableName}`) at line 235 and then passed to Kysely's `sql.table()` and `sql.ref()` at lines 257–296. While Kysely correctly double-quotes identifiers — preventing SQL injection — there is no upfront guard to reject nonsensical or adversarially crafted values.
51
+
52
+ Contrast: `SQLiteSchedulePersistence` (`src/scheduler/sqlite-persistence.ts:61`) validates its `tableName` with `^[a-zA-Z_][a-zA-Z0-9_]*$` and the Postgres *storage* adapter (`src/storage/postgres.ts:228`) validates its schema similarly. The omission in `postgres-persistence.ts` is an inconsistency in the defensive pattern.
53
+
54
+ **Impact:**
55
+ In the current implementation, `sql.table()` quoting prevents SQL injection. However, a consumer passing a malformed `tableName` (e.g. `"foo\x00bar"`, very long strings, or strings containing periods) will receive a confusing runtime database error rather than a clear upfront `Error`. If the Kysely dependency ever changes its quoting behaviour, this becomes a direct injection point.
56
+
57
+ **Remediation:**
58
+ Add the same regex guard used for `schema` immediately after line 153:
59
+ ```typescript
60
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.tableName)) {
61
+ throw new Error(`Invalid table name "${this.tableName}".`);
62
+ }
63
+ ```
64
+
65
+ ---
66
+
67
+ ### M2 — Non-Cryptographic ID Generation Used for Workflow Run Identifiers
68
+
69
+ **File:** `src/utils/id.ts:17`
70
+
71
+ **Description:**
72
+ `generateId()` constructs identifiers using `Date.now()` and `Math.random()`:
73
+
74
+ ```typescript
75
+ const randomPart = Math.random().toString(36).substring(2, 10);
76
+ ```
77
+
78
+ `Math.random()` is not a CSPRNG. V8's implementation is seeded with a 64-bit value derived from the system clock at startup; an attacker with knowledge of the process start time can reconstruct the PRNG state and predict future IDs. These IDs are used as primary keys for workflow runs and steps (`src/core/orchestrator.ts`).
79
+
80
+ **Impact:**
81
+ If run IDs are used for access-control purposes by a consumer (e.g. "share a link containing the run ID" patterns), predictable IDs enable enumeration and unauthorized access. The library itself does not enforce any access control on run IDs, so impact depends entirely on the consuming application's threat model.
82
+
83
+ **Remediation:**
84
+ Replace `Math.random()` with `crypto.randomBytes` (Node) or `crypto.getRandomValues` (Web Crypto), both available in Node.js 18+:
85
+ ```typescript
86
+ import { randomBytes } from 'node:crypto';
87
+ const randomPart = randomBytes(6).toString('base64url');
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Low Findings
93
+
94
+ ### L1 — Blanket Block of All IPv6 Literals in SSRF Protection
95
+
96
+ **File:** `src/events/webhook.ts:493–496`
97
+
98
+ **Description:**
99
+ The `isBlockedHost` function blocks **all** IPv6 address literals, not just private/reserved ones:
100
+
101
+ ```typescript
102
+ // Block all IPv6 address literals (wrapped in brackets by URL parser)
103
+ if (hostname.startsWith('[')) {
104
+ return true;
105
+ }
106
+ ```
107
+
108
+ This was added to prevent SSRF via `[::1]` and similar loopback bypasses (commit `48e5f0a`). While the intent is correct, the implementation also blocks legitimate public IPv6 targets such as `[2001:db8::1]`.
109
+
110
+ **Impact:**
111
+ Consumers cannot register webhook endpoints using public IPv6 addresses. This is a functional limitation, not a security vulnerability. As global IPv6 adoption grows, this will become increasingly restrictive.
112
+
113
+ **Remediation:**
114
+ Instead of blocking all bracket-wrapped addresses, parse the IPv6 address, expand it to full form, and check only against reserved ranges (loopback `::1`, ULA `fc00::/7`, link-local `fe80::/10`, documentation prefixes, etc.) using a library or the same range-check logic applied to IPv4.
115
+
116
+ ---
117
+
118
+ ### L2 — `allowInsecureUrls` Flag Lacks Production-Safety Warning
119
+
120
+ **File:** `src/events/webhook.ts:64–67`
121
+
122
+ **Description:**
123
+ The `allowInsecureUrls` option on `WebhookEventTransportConfig` relaxes the HTTPS requirement and bypasses URL scheme validation:
124
+
125
+ ```typescript
126
+ /**
127
+ * Whether to allow non-HTTPS URLs (default: false).
128
+ * Set to true only in development environments.
129
+ */
130
+ allowInsecureUrls?: boolean;
131
+ ```
132
+
133
+ The comment mentions development use, but there is no runtime enforcement or warning when this flag is enabled in a context that looks like production (e.g. `NODE_ENV=production`).
134
+
135
+ **Impact:**
136
+ A consumer who copies a development configuration to production silently downgrades all webhooks to plain HTTP, exposing payloads (which may contain workflow results and metadata) to network interception, and disabling part of the SSRF protection.
137
+
138
+ **Remediation:**
139
+ Emit a logger warning at construction time when `allowInsecureUrls: true` is set, e.g.:
140
+ ```typescript
141
+ if (this.allowInsecureUrls) {
142
+ this.logger.warn('allowInsecureUrls is enabled. Do not use this in production.');
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ### L3 — No Rate Limiting on Outbound Webhook Dispatch
149
+
150
+ **File:** `src/events/webhook.ts:465–480`
151
+
152
+ **Description:**
153
+ The webhook transport implements concurrency limiting (default 50 concurrent requests) and a queue, but no per-endpoint rate limiting or per-second emission cap. A burst of workflow events can cause hundreds of webhook deliveries in a short window.
154
+
155
+ **Impact:**
156
+ Could trigger rate limiting or bans from external webhook receivers. Does not directly introduce an inbound attack surface (the library sends, not receives), but could cause unintended denial-of-service toward third-party services.
157
+
158
+ **Remediation:**
159
+ Add an optional `rateLimit` configuration (e.g. `maxRequestsPerSecond`) per endpoint or globally, with token-bucket or leaky-bucket logic.
160
+
161
+ ---
162
+
163
+ ## Secrets & Credentials Exposure
164
+
165
+ **Result: None found.**
166
+
167
+ - `git grep` across all `*.ts`, `*.js`, `*.json`, `*.yaml`, `*.yml` files for `password`, `secret`, `api_key`, `token`, and `private_key` returned only:
168
+ - **`src/events/webhook.ts:22–23`** — `secret?: string` type definition for HMAC signing (no value)
169
+ - **`src/events/webhook.ts:116`** — JSDoc comment example: `secret: process.env.WEBHOOK_SECRET` (correctly references an environment variable, not a hardcoded value)
170
+ - **`src/planning/planner.ts`, `src/planning/types.ts`** — `tokensPerStep`, `tokens` — LLM token budget fields, not credentials
171
+ - **`dist/` build artefacts** — mirror of source, no embedded values
172
+
173
+ - No `.env` files exist in the repository at any path.
174
+ - `git log --all -p -- "*.env" "*.pem" "*.key"` returned no output — no credential files were ever committed to git history.
175
+
176
+ ---
177
+
178
+ ## Recommendations
179
+
180
+ The following are ordered by risk impact:
181
+
182
+ 1. **[Medium — M1] Validate `tableName` in `PostgresSchedulePersistence`.**
183
+ Add a regex guard (`^[a-zA-Z_][a-zA-Z0-9_]{0,62}$`) for `tableName` at `src/scheduler/postgres-persistence.ts:153`, matching the existing pattern in `sqlite-persistence.ts:61` and `storage/postgres.ts:228`. This closes the inconsistency and provides a clean failure mode.
184
+
185
+ 2. **[Medium — M2] Replace `Math.random()` with a CSPRNG in ID generation.**
186
+ Replace `Math.random()` at `src/utils/id.ts:17` with `crypto.getRandomValues` or Node's `crypto.randomBytes`. The timestamp prefix can remain. This is low-effort and eliminates predictability of workflow run IDs if consumers use them for access decisions.
187
+
188
+ 3. **[Low — L2] Add a runtime warning for `allowInsecureUrls: true`.**
189
+ Log a warning through the configured logger in `WebhookEventTransport`'s constructor when `allowInsecureUrls` is set. This is a single-line addition that prevents silent production misconfiguration.
190
+
191
+ 4. **[Low — L1] Narrow IPv6 blocking to reserved ranges only.**
192
+ Replace the blanket `hostname.startsWith('[')` block in `isBlockedHost` (`webhook.ts:493`) with targeted checks for `[::1]`, `[fc00:...]`, `[fe80:...]`, and other RFC-reserved IPv6 prefixes, allowing legitimate public IPv6 webhook endpoints.
193
+
194
+ 5. **[Low — L3] Document or implement outbound webhook rate limiting.**
195
+ Either add an optional `rateLimit` configuration to `WebhookEventTransport`, or explicitly document in JSDoc that consumers are responsible for choosing receivers that can handle burst traffic.
196
+
197
+ 6. **[Informational] Add explicit authorization enforcement documentation.**
198
+ The Socket.IO transport (`src/events/socketio.ts`) requires consumers to provide an `authorize` callback. Add JSDoc `@throws` documentation and an example showing what happens when no `authorize` callback is supplied, to prevent consumers from accidentally running with open subscriptions.
199
+
200
+ 7. **[Informational] Consider documenting CORS requirements for Socket.IO consumers.**
201
+ The library accepts an already-constructed `SocketIOServer` (`src/events/socketio.ts:45`) and does not configure CORS. Add a README section or JSDoc example showing the minimum recommended CORS configuration to avoid consumers defaulting to wildcard origins.
202
+
203
+ 8. **[Informational] Run `npm audit` in CI.**
204
+ The production dependency surface is minimal (only `cron-parser` and `re2`), but automated vulnerability scanning should be enforced in the CI pipeline to catch transitive dependency CVEs early.
205
+
206
+ ---
207
+
208
+ *Audit performed: 2026-04-03. Branch: `main` (HEAD `4d9838a`). No source files were modified during this audit.*