Node Extensions
This document describes how TWIN Node discovers, loads, initialises, and shuts down extensions, and how protocol-based module loading works for local files, npm packages, and remote HTTPS modules.
Extension Entry Point
Node reads the extensions environment variable (from INodeEnvironmentVariables) as a comma-separated list of module identifiers. For each entry, Node attempts to load optional lifecycle methods:
extensionInitialise(envVars, nodeEngineConfig)extensionInitialiseEngine(engineCore)extensionInitialiseEngineServer(engineCore, engineServer)extensionShutdown()
These are orchestrated in packages/node-core/src/builders/extensionsBuilder.ts.
Lifecycle Timing
The extension lifecycle is split across runtime phases:
- Configuration phase:
extensionInitialisecan mutate node engine configuration before engine creation. - Engine phase:
extensionInitialiseEngineruns after engine construction. - Engine-server phase:
extensionInitialiseEngineServerruns after server construction. - Shutdown phase:
extensionShutdownruns during graceful termination.
Extension initialisation state is tracked to avoid duplicate initialisation in-process.
Extension Hook Timing Relative to Node Startup
Module Protocols
Protocol handling is implemented via overrideModuleImport and utility functions in packages/node-core/src/utils.ts.
Supported module forms:
- Local file/module path:
./my-extension.mjs,../path/ext.mjs, absolute path, orfile://... - npm protocol:
npm:@scope/packageornpm:package@version - HTTPS protocol:
https://host/path/extension.mjs - Default package resolution: plain package name lookup with fallback into extension cache
Rejected protocol:
http://is blocked and treated as insecure.
npm Protocol Flow
For npm: entries, Node:
- Resolves a protocol-specific cache directory.
- Checks if the package already exists in cache.
- If missing, installs the package under cache using npm.
- Resolves package entry point and returns module path.
This provides repeatable extension loading without modifying the application repository's dependency manifests.
HTTPS Protocol Flow
For https: entries, Node:
- Hashes the URL to produce a stable cache filename.
- Checks cached file and metadata.
- Applies TTL and force-refresh logic.
- Downloads content with max-size enforcement.
- Writes file plus metadata for future reuse.
Security and operational controls are applied through extension cache settings in environment variables.
Cache and Security Controls
Extension-related node environment settings:
{
"extensions": "Comma-separated list of extension module identifiers",
"extensionsMaxSizeMb": "Maximum HTTPS extension download size in MB",
"extensionsClearCache": "Clear extension cache on startup",
"extensionsCacheDirectory": "Custom extension cache directory",
"extensionsCacheTtlHours": "HTTPS extension cache TTL in hours",
"extensionsForceRefresh": "Force refresh cached extensions"
}
Default values are documented in INodeEnvironmentVariables and the extension loader utilities.
Example Extension
export async function extensionInitialise(envVars, nodeEngineConfig) {
nodeEngineConfig.debug = nodeEngineConfig.debug ?? false;
}
export async function extensionInitialiseEngine(engineCore) {
await engineCore.logInfo('Extension engine init');
}
export async function extensionInitialiseEngineServer(engineCore, engineServer) {
await engineCore.logInfo('Extension server init');
}
export async function extensionShutdown() {
// cleanup resources
}
Operational Guidance
- Prefer pinned npm versions for deterministic deployments.
- Use HTTPS sources only from trusted origins.
- Use cache TTL and force refresh intentionally during rollout.
- Keep extension initialisers idempotent where possible.
- Handle missing optional hooks gracefully; Node already treats them as optional.