src/domain/local-n8n-sidecar.js
Rama main · réplica de lectura del repositorio original.
import { validateLocalDockerHost } from "../infrastructure/local-process.js";
import { validateSiwcAuthBinding, validateSiwcRegistrationId, validateSiwcRuntimeId } from "./safety.js";
import { validateDockerName } from "./validation.js";
import { validateInstallId, validateSha256Verifier } from "./local-endpoints.js";
export const LOCAL_N8N_SIDECAR_TARGET = "n8n-openai-oauth";
export const LOCAL_N8N_SIDECAR_HOSTNAME = "n8n-openai-oauth";
export const LOCAL_N8N_SIDECAR_PORT = 10_531;
export const LOCAL_N8N_SIDECAR_ENDPOINT =
"http://n8n-openai-oauth:10531/v1";
const DOCKER_OBJECT_ID_PATTERN = /^[a-f0-9]{64}$/u;
const AUTH_GENERATION_MAX_BYTES = 128;
export function validateDockerObjectId(value, label = "Docker object") {
if (typeof value !== "string" || !DOCKER_OBJECT_ID_PATTERN.test(value)) {
throw new TypeError(`${label} identity is invalid.`);
}
return value;
}
export function createLocalN8nSidecarPlan({
dockerHost,
n8nContainerId,
n8nContainerName,
dockerNetworkId,
networkName,
authBinding,
}) {
return {
kind: "n8n-sidecar",
target: LOCAL_N8N_SIDECAR_TARGET,
label: "Self-hosted n8n bridge",
endpoint: LOCAL_N8N_SIDECAR_ENDPOINT,
baseUrl: LOCAL_N8N_SIDECAR_ENDPOINT,
protocol: "openai-v1",
upstreamAuth: "chatgpt-oauth",
dockerHost: validateLocalDockerHost(dockerHost),
n8nContainerId: validateDockerObjectId(
n8nContainerId,
"n8n container",
),
n8nContainerName: validateDockerName(n8nContainerName),
dockerNetworkId: validateDockerObjectId(
dockerNetworkId,
"Docker network",
),
networkName: validateDockerName(networkName),
authBinding: validateSiwcAuthBinding(authBinding),
managedPath: "~/.relmio/local/n8n-openai-oauth",
hostPublication: "none",
unofficial: false,
};
}
export function normalizeLocalN8nSidecarPlan(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new TypeError("The local n8n sidecar plan is invalid.");
}
const normalized = createLocalN8nSidecarPlan({
dockerHost: value.dockerHost,
n8nContainerId: value.n8nContainerId,
n8nContainerName: value.n8nContainerName,
dockerNetworkId: value.dockerNetworkId,
networkName: value.networkName,
authBinding: value.authBinding,
});
for (const [name, expected] of Object.entries(normalized)) {
if (name === "authBinding") {
for (const [field, bound] of Object.entries(expected)) {
if (value.authBinding?.[field] !== bound) {
throw new TypeError("The local n8n sidecar plan is invalid.");
}
}
} else if (value[name] !== expected) {
throw new TypeError("The local n8n sidecar plan is invalid.");
}
}
return normalized;
}
export function createLocalN8nSidecarDockerfile({ installId }) {
const safeInstallId = validateInstallId(installId);
return `FROM node:24-bookworm-slim
ARG RELMIO_INSTALL_ID=${safeInstallId}
LABEL io.relmio.managed="true" \\
io.relmio.target="${LOCAL_N8N_SIDECAR_TARGET}" \\
io.relmio.install="${safeInstallId}"
WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts && npm cache clean --force
COPY --chown=node:node services/ ./services/
COPY --chown=node:node gateway/ ./gateway/
COPY --chown=node:node infrastructure/ ./infrastructure/
USER node
ENTRYPOINT ["node", "/app/gateway/openai-oauth-sidecar.mjs"]
`;
}
export function createLocalN8nSidecarDockerignore() {
return "**\n!Dockerfile\n!package.json\n!package-lock.json\n!services/\n!services/siwc-session.mjs\n!services/siwc-handoff.mjs\n!services/local-integration-lifecycle-lock.js\n!services/codex-images.mjs\n!services/model-discovery.mjs\n!gateway/\n!gateway/openai-oauth-sidecar.mjs\n!infrastructure/\n!infrastructure/local-process.js\n!infrastructure/process-identity.js\n";
}
export function createLocalN8nSidecarComposeFile({
installId,
networkName,
registrationId,
tokenSha256,
}) {
const safeInstallId = validateInstallId(installId);
const safeNetworkName = validateDockerName(networkName);
const safeRegistrationId = validateSiwcRegistrationId(registrationId);
const safeRuntimeId = validateSiwcRuntimeId(safeInstallId);
const safeVerifier = validateSha256Verifier(tokenSha256);
const image = `relmio-n8n-openai-oauth-${safeInstallId}:local`;
return `services:
openai-oauth:
image: ${image}
build:
context: .
dockerfile: Dockerfile
args:
RELMIO_INSTALL_ID: "${safeInstallId}"
labels:
io.relmio.managed: "true"
io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
io.relmio.install: "${safeInstallId}"
restart: unless-stopped
init: true
environment:
N8N_OPENAI_OAUTH_HOME: /home/node/.relmio-siwc
RELMIO_REGISTRATION_ID: "${safeRegistrationId}"
RELMIO_RUNTIME_ID: "${safeRuntimeId}"
RELMIO_GATEWAY_TOKEN_SHA256: "${safeVerifier}"
volumes:
- siwc-store:/home/node/.relmio-siwc
expose:
- "10531"
networks:
n8n-shared:
aliases:
- ${LOCAL_N8N_SIDECAR_HOSTNAME}
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=16m,mode=1777
- /home/node/.local:uid=1000,gid=1000,mode=0700
pids_limit: 128
mem_limit: 512m
cpus: 1.0
healthcheck:
test:
- CMD
- node
- -e
- 'fetch("http://127.0.0.1:10531/health").then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))'
interval: 10s
timeout: 5s
retries: 6
start_period: 20s
labels:
io.relmio.managed: "true"
io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
io.relmio.install: "${safeInstallId}"
credential-seed:
image: ${image}
pull_policy: never
profiles:
- relmio-credential-seed
restart: "no"
network_mode: none
user: "0:0"
entrypoint:
- /bin/sh
- -c
command:
- |
set -eu
owner="$$(stat -c '%u:%g:%a' /run/relmio-auth)"
if [ "$$owner" = "0:0:755" ] || [ "$$owner" = "0:0:700" ]; then
chmod 0700 /run/relmio-auth
chown 1000:1000 /run/relmio-auth
elif [ "$$owner" != "1000:1000:700" ]; then
exit 1
fi
[ "$$(stat -c '%u:%g:%a' /run/relmio-auth)" = "1000:1000:700" ]
volumes:
- siwc-store:/run/relmio-auth
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
read_only: true
pids_limit: 32
mem_limit: 64m
cpus: 0.25
logging:
driver: "none"
labels:
io.relmio.managed: "true"
io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
io.relmio.install: "${safeInstallId}"
networks:
n8n-shared:
external: true
name: ${safeNetworkName}
volumes:
siwc-store:
labels:
io.relmio.managed: "true"
io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
io.relmio.install: "${safeInstallId}"
`;
}