CodeVetter · Repo Unpack · Behavior traces
n8n-io/n8n
Three flows traced end-to-end. (1) Startup: bin/n8n -> CommandRegistry.execute -> Start.init (DB connect/migrate, license, community packages, roles, modules) -> Start.run (settings, server.start, schedulers, workflow activation). (2) Webhook: express route -> WebhookRequestHandler -> LiveWebhooks/WebhookService -> workflow execution -> WebhookResponse. (3) Execution: REST run -> WorkflowRunner -> WorkflowExecute -> lifecycle hooks -> DB persistence. Shutdown and worker boot are also documented.
- Startup order: bin/n8n (version check, dotenv, dist/config) -> Container.get(CommandRegistry).execute() -> imports commands/start.js -> parses flags via zod -> Container.get(Start) -> init() -> run().
- BaseCommand.init runs first inside Start.init: installOutboundProxyAgents -> ErrorReporter.init (Sentry) -> signal handlers -> LoadNodesAndCredentials.init -> optional RedisLockService -> DbConnection.init -> server.init -> DbConnection.migrate -> instanceSettings.initialize.
- Start.init continues: DeprecationService.warn -> PollJobProvider.init -> instance role resolution (regular=leader else initOrchestration) -> JwtService/BinaryDataConfig init -> initLicense -> ensureMultiMainLicensed -> initCommunityPackages -> PubSubRegistry.init (rewire) -> AuthRolesService.init + RoleCacheService.refreshCache (main only) -> BinaryDataService -> WaitTracker -> CredentialsOverwrites -> dedup -> external hooks -> workflow history -> generateStaticAssets -> moduleRegistry.initModules -> AuthHandlerRegistry -> MultiMainSetup handlers -> executionContextHookRegistry -> postProcessLoaders.
- Start.run: load SettingsRepository rows where loadOnStartup into convict config -> optional SQLite VACUUM -> server.start() -> init pruning/history-compaction/statistics services -> register mainSystemTasks + SystemTaskRunner -> DurableScheduler.start -> EnqueuedExecutionRecoveryService (regular mode) -> either workflow-publication outbox consumer or ActiveWorkflowManager.init -> releaseTypes -> print editor URL -> TTY 'o' key opens browser.
- Server.start -> configure(): Prometheus metrics (optional) -> frontendService.getSettings + external hook -> PostHog -> loadPublicApiVersions mounts public routers -> register auth strategies -> cookieParser -> CSP middleware (deliberately after webhook routes) -> Push.setupPushHandler -> CollaborationService if websocket -> ScalingService.setupQueue in queue mode -> registerAdditionalControllers -> PubSubRegistry.init -> ControllerRegistry.activate -> eventBus.initialize -> workflow indexing -> preset-credentials route -> protectTypeFiles -> static/history handlers.
- Webhook request flow: route registered on AbstractServer before bodyParser -> createWebhookHandlerFor(LiveWebhooks|WaitingWebhooks|TestWebhooks|...) -> WebhookRequestHandler.handleRequest: reject non-HTTP-methods, CORS preflight handling, then webhookManager.executeWebhook -> response dispatched as WebhookResponse (no-response/static/stream) or legacy {data,responseCode,headers}.
- Webhook resolution persists through WebhookService: findTriggerWebhooksByPath queries webhook_entity rows, pickMatchingTemplate selects the best segment match, storeWebhook inserts rows; on activation ActiveWorkflowManager registers live paths and deactivation calls deleteWorkflowWebhooks.
- Manual/REST execution: POST /workflows/:workflowId/run (or executions retry) -> WorkflowRunner.run -> build additionalData via WorkflowExecuteAdditionalData -> pick lifecycle hooks (getLifecycleHooksForRegularMain/ScalingMain/ScalingWorker) -> new WorkflowExecute(...).run(...) returns PCancelable -> processRunExecutionData walks the execution stack -> nodeExecute functions resolve params, credentials, expressions -> hooks persist via ExecutionPersistence/saveExecutionProgress -> Push broadcasts progress to the editor.
- Worker boot: 'worker' command forces executions.mode='queue' -> setConcurrency (env N8N_CONCURRENCY_PRODUCTION_LIMIT or --concurrency, warns <5) -> init chain (license, community packages, credentials overwrites, binary data, dedup, event bus) -> ScalingService.setupQueue -> Redis subscriber on command channel -> run() optionally starts WorkerServer (health/overwrites/metrics) then setupWorker(concurrency) consumes Bull jobs; process then idles forever.
- Graceful shutdown (Start.stopProcess): removeAllQueuedWorkflowActivations -> WaitTracker.stopTracking -> externalHooks 'n8n.stop' -> removeAllNonWebhookTriggerWorkflows -> MultiMainSetup.shutdown (if multi-main) -> Publisher/Subscriber.shutdown (queue mode) -> EventService 'instance-stopped' -> ActiveExecutions.shutdown -> MessageEventBus.close -> exitSuccessFully; SIGTERM/SIGINT handlers are installed in BaseCommand.init.