External Modules & Custom Applications
Maintain non-trivial business logic, domain algorithms, or entire legacy applications in separate repositories. Load and execute them seamlessly inside VaakLoom workflows with Hub-managed runtime or your own CI/CD pipelines.
Why External Code Modules?
While low-code visual workflows excel at API routing, branching, rate-limiting, and transformations, real enterprise systems often need heavy custom logic:
- Domain Algorithms: Proprietary scoring models, pricing engines, or risk assessment matrices.
- Separate Lifecycles: Teams need dedicated git repositories with independent pull requests, CI/CD, and semantic versioning.
- Multi-Function Modules: Export multiple named functions or entire service contracts from a single repository.
- Polyglot Microservices: Existing Python, Go, Java, or Rust applications that you want to integrate via high-speed HTTP sidecars.
Three Loading Mechanisms
VaakLoom supports three distinct source protocols for loading external code into workflow steps:
npm:<package>@<version>
Loads published npm packages in-process via dynamic import(). Supports version pinning or latest resolution. Zero infrastructure overhead.
source: "npm:@acme/pricing-rules@2.1.0"
git:<url>#<ref>
References a private or public git repo by branch, tag, or commit. Private repos authenticate securely using SSH deploy keys or access tokens.
source: "git:https://github.com/acme/rules.git#v1.4"
http:<endpoint>
Invokes a customer-managed container or microservice via HTTP sidecar contract. Completely isolated process space in any programming language.
source: "http://pricing-sidecar:9090"
Runtime Management Modes
You choose how your extension runtime is managed:
- Hub-Managed (In-Process): The VaakLoom Hub installs, caches, and runs the module within its high-performance JavaScript runtime. Ideal for fast development with zero cloud infrastructure management.
- Self-Managed (Sidecar Container): You deploy the extension container on Kubernetes or your container platform. Workflows route step payloads to the sidecar over localhost or internal DNS.
-
Custom CI/CD Webhook Integration: Configure your own external CI/CD pipeline (GitHub Actions, GitLab CI, ArgoCD, Jenkins). When your pipeline builds or tags a release, it sends a webhook to VaakLoom:
VaakLoom automatically pulls the artifact, refreshes the module in memory, and reloads active workflows with zero downtime.POST /hub/extensions/:id/deploy-webhook Headers: x-webhook-secret: <YOUR_SECRET> Body: { "ref": "refs/tags/v2.0.0", "commit": "abc1234", "sender": "ci" }
Writing an Extension Module
An external extension repository exports one or more functions matching the standard step handler contract:
// src/index.ts in your custom repository
export async function calculateRisk({ input, ctx }: { input: any; ctx: any }) {
const score = input.creditScore * 0.7 + input.annualIncome * 0.0001;
return {
approved: score > 650,
riskTier: score > 750 ? "LOW" : "MEDIUM",
computedAt: new Date().toISOString(),
};
}
export async function validateKyc({ input, ctx }: { input: any; ctx: any }) {
return {
verified: Boolean(input.passportNumber && input.country),
};
}
// Default export if called without specific operation
export default calculateRisk;
Using Extensions in Workflows
Declare the extension in your WorkflowSpec and invoke its functions using the ext:<moduleId>:<function> syntax:
{
"version": 1,
"id": "loan-application-workflow",
"route": { "method": "POST", "path": "/loans/apply" },
"extensions": [
{
"moduleId": "risk-engine",
"source": "npm:@acme/risk-engine@1.2.0",
"runtime": "hub-managed"
}
],
"nodes": [
{
"type": "step",
"step": {
"id": "step1_kyc",
"kind": "ext:risk-engine:validateKyc",
"inputFrom": "input"
}
},
{
"type": "step",
"step": {
"id": "step2_score",
"kind": "ext:risk-engine:calculateRisk",
"inputFrom": "input"
}
}
]
}
Per-Workflow Authentication & Custom JWT
Each workflow can define its own authentication requirements independently:
- platform (default): Requires a Platform API key with
api:invokepermission. - custom_jwt: Validates customer JWT tokens with signature check and arbitrary required key-value claims (e.g.
role: "underwriter",iss: "https://auth.acme.com"). Claims are automatically decoded and injected into context attributes ({{attrs.jwt.sub}},{{attrs.claims.role}}). - public: Completely unauthenticated public endpoint.
{
"version": 1,
"id": "secured-finance-api",
"route": { "method": "POST", "path": "/finance/payout" },
"auth": {
"mode": "custom_jwt",
"jwtSecret": "acme-hmac-secret-256",
"requiredClaims": {
"role": "financial-officer",
"department": "treasury"
}
},
"nodes": [ ... ]
}