Crosslink’s signaling nodes are horizontally scalable with Redis. Relay channels are
ephemeral and process-local, with region catalogs and host-side fallback for scale-out.
Crosslink does not publish Docker images or Dockerfiles today. Each service is a plain Node.js CLI (@crosslink/signaling’s crosslink-signaling bin, @crosslink/relay’s crosslink-relay bin) built with npm run build, so it packages into a container however you already build Node services — copy services/signaling (or services/relay) and its built dist/ into a node:20-slim (or similar) base image and run the bin.Both services require a bind before phones can reach them from anywhere but the machine they’re on; use your normal container port-mapping.
Shared secret hosts must present; required unless per-machine dev tokens apply
CROSSLINK_RELAY_CLIENT_TOKEN
(none)
Optional shared secret clients must present to attach
CROSSLINK_RELAY_MAX_CLIENTS
(unset)
Max clients per relay channel
CROSSLINK_RELAY_MAX_CHANNELS
1024
Max allocated channels per process
CROSSLINK_RELAY_MAX_BYTES
(unlimited)
Lifetime traffic cap per channel
CROSSLINK_RELAY_BYTES_PER_SEC
(unlimited)
Token-bucket rate per channel
CROSSLINK_RELAY_PUBLIC_URL
(none)
Public HTTPS URL for this relay region
CROSSLINK_RELAY_REGIONS
(none)
JSON array of regional URLs and priorities
Outside of local development, both services refuse to start without an auth token (CROSSLINK_SIGNALING_TOKEN / CROSSLINK_RELAY_TOKEN) or the per-machine dev tokens written to .crosslink-data/dev-tokens.json. Set these explicitly for any deployment other services will connect to.
Signaling can be horizontally scaled when every replica uses the same Redis
deployment. Relay WebSockets require channel affinity; deploy independent regional
relay pools and let hosts allocate using ordered regional fallback:
upstream signaling { server 10.0.0.1:8081; server 10.0.0.2:8081; server 10.0.0.3:8081;}upstream relay { server 10.0.0.1:8082; server 10.0.0.2:8082; server 10.0.0.3:8082;}
Do not round-robin an allocated relay channel across processes. Either keep
WebSocket affinity for the channel or publish each relay pool as a distinct regional
allocation endpoint.
Relay channel state and signaling presence are ephemeral. Redis persistence is not
required for correctness after a restart, but production configuration must be
recoverable. Back up:
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Crosslink","colors":{"primary":"#6366f1","light":"#818cf8","dark":"#4f46e5"},"logo":{"light":"https://mintcdn.com/crosslink/z7NspvKrsX5Mko2W/crosslink.svg?fit=max\u0026auto=format\u0026n=z7NspvKrsX5Mko2W\u0026q=85\u0026s=6ee500820cebb5255a624d81b345c9f1","dark":"https://mintcdn.com/crosslink/z7NspvKrsX5Mko2W/crosslink.svg?fit=max\u0026auto=format\u0026n=z7NspvKrsX5Mko2W\u0026q=85\u0026s=6ee500820cebb5255a624d81b345c9f1"},"favicon":"https://mintcdn.com/crosslink/z7NspvKrsX5Mko2W/crosslink.svg?fit=max\u0026auto=format\u0026n=z7NspvKrsX5Mko2W\u0026q=85\u0026s=6ee500820cebb5255a624d81b345c9f1","navbar":{"links":[{"href":"https://github.com/jacobpowaza/crosslink","label":"GitHub"}],"primary":{"type":"button","label":"Get Started","href":"/quickstart"}},"footer":{"socials":{"github":"https://github.com/jacobpowaza/crosslink"}},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"crosslink","hasAudioNarration":false,"actualSubdomain":"crosslink","customDomains":[],"gitSource":{"type":"github","owner":"jacobpowaza","repo":"crosslink","deployBranch":"main","contentDirectory":"docs","isPrivate":false,"mountPath":"","label":""},"chroma":{"latestBuiltDeploymentHistoryId":"6a9237862b17eafb9e0b8b19","collectionId":"22322d64-7e1b-4a00-aa0c-f000b12ccc8e","liveDeploymentHistoryId":"6a9237862b17eafb9e0b8b19","schemaVersion":"v3"},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6a9237862b17eafb9e0b8b19","clientVersion":"0.0.3908","searchFilterCounts":[{"language":"default","tag":"GUIDES","count":10},{"language":"default","tag":"REFERENCE","count":8},{"language":"default","tag":"CONCEPTS","count":7},{"language":"default","tag":"CLIENT","count":6},{"language":"default","tag":"SECURITY","count":6},{"language":"default","tag":"BUILD","count":5},{"language":"default","tag":"CONNECTIONS","count":4},{"language":"default","tag":"RESOURCES","count":4},{"language":"default","tag":"GET STARTED","count":3}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-branches-v2-autocommit","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-branches-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"teamspace","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"client-snapshot-docs-config","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"guides/self-hosting","slug":"/guides/self-hosting","pageMetadata":{"title":"Self-Hosting","description":"Running your own signaling and relay servers","href":"/guides/self-hosting"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Running your own signaling and relay servers\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={code:`code`,em:`em`,input:`input`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Table,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`overview`,children:`Overview`}),`\n`,_jsx(_components.p,{children:`Crosslink’s signaling nodes are horizontally scalable with Redis. Relay channels are\nephemeral and process-local, with region catalogs and host-side fallback for scale-out.`}),`\n`,_jsx(Heading,{level:`2`,id:`quick-start`,children:`Quick start`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`4`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`git`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` clone`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` https://github.com/jacobpowaza/crosslink.git`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#DCDCAA`},children:`cd`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` crosslink`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`npm`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` install`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`npm`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` run`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` stack`})]}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`This starts both services on localhost:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Signaling: `,_jsx(_components.code,{children:`ws://127.0.0.1:8081`})]}),`\n`,_jsxs(_components.li,{children:[`Relay: `,_jsx(_components.code,{children:`http://127.0.0.1:8082`})]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`services`,children:`Services`}),`\n`,_jsx(Heading,{level:`3`,id:`signaling-service`,children:`Signaling service`}),`\n`,_jsx(_components.p,{children:`The signaling service helps devices find each other and route pairing codes.`}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`What it sees:`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Hashed pairing codes`}),`\n`,_jsx(_components.li,{children:`Opaque signed blobs (not readable without private keys)`}),`\n`,_jsx(_components.li,{children:`Device fingerprints (for routing)`}),`\n`]}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`What it cannot see:`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Plaintext content`}),`\n`,_jsx(_components.li,{children:`Session keys`}),`\n`,_jsx(_components.li,{children:`Private keys`}),`\n`]}),`\n`,_jsx(Heading,{level:`3`,id:`relay-service`,children:`Relay service`}),`\n`,_jsx(_components.p,{children:`The relay service forwards encrypted traffic when a direct connection isn’t possible.`}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`What it sees:`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Ciphertext (encrypted frames)`}),`\n`]}),`\n`,_jsx(_components.p,{children:_jsx(_components.strong,{children:`What it cannot see:`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Plaintext content`}),`\n`,_jsx(_components.li,{children:`Session keys`}),`\n`,_jsx(_components.li,{children:`Message content`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`building-your-own-container-image`,children:`Building your own container image`}),`\n`,_jsxs(_components.p,{children:[`Crosslink does not publish Docker images or Dockerfiles today. Each service is a plain Node.js CLI (`,_jsx(_components.code,{children:`@crosslink/signaling`}),`’s `,_jsx(_components.code,{children:`crosslink-signaling`}),` bin, `,_jsx(_components.code,{children:`@crosslink/relay`}),`’s `,_jsx(_components.code,{children:`crosslink-relay`}),` bin) built with `,_jsx(_components.code,{children:`npm run build`}),`, so it packages into a container however you already build Node services — copy `,_jsx(_components.code,{children:`services/signaling`}),` (or `,_jsx(_components.code,{children:`services/relay`}),`) and its built `,_jsx(_components.code,{children:`dist/`}),` into a `,_jsx(_components.code,{children:`node:20-slim`}),` (or similar) base image and run the bin.`]}),`\n`,_jsx(_components.p,{children:`Both services require a bind before phones can reach them from anywhere but the machine they’re on; use your normal container port-mapping.`}),`\n`,_jsx(Heading,{level:`2`,id:`environment-variables`,children:`Environment variables`}),`\n`,_jsx(Heading,{level:`3`,id:`signaling`,children:`Signaling`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Variable`}),_jsx(_components.th,{children:`Default`}),_jsx(_components.th,{children:`Description`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`PORT`})}),_jsx(_components.td,{\"data-numeric\":`true`,children:_jsx(_components.code,{children:`8081`})}),_jsx(_components.td,{children:`HTTP/WebSocket server port`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`HOST`})}),_jsx(_components.td,{\"data-numeric\":`true`,children:_jsx(_components.code,{children:`0.0.0.0`})}),_jsx(_components.td,{children:`Bind address`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_SIGNALING_TOKEN`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsx(_components.td,{children:`Shared secret hosts must present; required unless per-machine dev tokens apply`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_REGION`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsx(_components.td,{children:`Optional region label included in presence data`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_REDIS_URL`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsxs(_components.td,{children:[`Redis URL for shared TTL state and cross-node pub/sub; use `,_jsx(_components.code,{children:`rediss://`}),` in production`]})]})]})]}),`\n`,_jsx(Heading,{level:`3`,id:`relay`,children:`Relay`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Variable`}),_jsx(_components.th,{children:`Default`}),_jsx(_components.th,{children:`Description`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`PORT`})}),_jsx(_components.td,{\"data-numeric\":`true`,children:_jsx(_components.code,{children:`8082`})}),_jsx(_components.td,{children:`HTTP/WebSocket server port`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`HOST`})}),_jsx(_components.td,{\"data-numeric\":`true`,children:_jsx(_components.code,{children:`0.0.0.0`})}),_jsx(_components.td,{children:`Bind address`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_TOKEN`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsx(_components.td,{children:`Shared secret hosts must present; required unless per-machine dev tokens apply`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_CLIENT_TOKEN`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsx(_components.td,{children:`Optional shared secret clients must present to attach`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_MAX_CLIENTS`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(unset)`})}),_jsx(_components.td,{children:`Max clients per relay channel`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_MAX_CHANNELS`})}),_jsx(_components.td,{\"data-numeric\":`true`,children:_jsx(_components.code,{children:`1024`})}),_jsx(_components.td,{children:`Max allocated channels per process`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_MAX_BYTES`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(unlimited)`})}),_jsx(_components.td,{children:`Lifetime traffic cap per channel`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_BYTES_PER_SEC`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(unlimited)`})}),_jsx(_components.td,{children:`Token-bucket rate per channel`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_PUBLIC_URL`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsx(_components.td,{children:`Public HTTPS URL for this relay region`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`CROSSLINK_RELAY_REGIONS`})}),_jsx(_components.td,{children:_jsx(_components.em,{children:`(none)`})}),_jsx(_components.td,{children:`JSON array of regional URLs and priorities`})]})]})]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`Outside of local development, both services refuse to start without an auth token (`,_jsx(_components.code,{children:`CROSSLINK_SIGNALING_TOKEN`}),` / `,_jsx(_components.code,{children:`CROSSLINK_RELAY_TOKEN`}),`) or the per-machine dev tokens written to `,_jsx(_components.code,{children:`.crosslink-data/dev-tokens.json`}),`. Set these explicitly for any deployment other services will connect to.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`production-considerations`,children:`Production considerations`}),`\n`,_jsx(Heading,{level:`3`,id:`tls-termination`,children:`TLS termination`}),`\n`,_jsx(_components.p,{children:`Use a reverse proxy (nginx, Caddy, Cloudflare) for TLS:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`16`,language:`nginx`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`nginx`,children:_jsxs(_components.code,{language:`nginx`,numberOfLines:`16`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# nginx.conf`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` listen `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`443`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` ssl;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server_name `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`signaling.example.com;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` ssl_certificate `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`/etc/letsencrypt/live/signaling.example.com/fullchain.pem;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` ssl_certificate_key `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`/etc/letsencrypt/live/signaling.example.com/privkey.pem;`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` location`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:` / `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_pass `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`http://127.0.0.1:8081;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_http_version `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`1.1`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Upgrade $`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`http_upgrade`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Connection `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"upgrade\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` proxy_set_header `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Host $`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`host`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Heading,{level:`3`,id:`load-balancing`,children:`Load balancing`}),`\n`,_jsx(_components.p,{children:`Signaling can be horizontally scaled when every replica uses the same Redis\ndeployment. Relay WebSockets require channel affinity; deploy independent regional\nrelay pools and let hosts allocate using ordered regional fallback:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`11`,language:`nginx`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`nginx`,children:_jsxs(_components.code,{language:`nginx`,numberOfLines:`11`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`upstream`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:` signaling `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` 10.0.0.1:8081;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` 10.0.0.2:8081;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` 10.0.0.3:8081;`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`upstream`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:` relay `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` 10.0.0.1:8082;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` 10.0.0.2:8082;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:` server`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` 10.0.0.3:8082;`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Warning,{children:_jsx(_components.p,{children:`Do not round-robin an allocated relay channel across processes. Either keep\nWebSocket affinity for the channel or publish each relay pool as a distinct regional\nallocation endpoint.`})}),`\n`,_jsx(Heading,{level:`3`,id:`monitoring`,children:`Monitoring`}),`\n`,_jsx(_components.p,{children:`Monitor these metrics:`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Metric`}),_jsx(_components.th,{children:`Warning threshold`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Connection count`}),_jsx(_components.td,{children:`Sustained growth with no corresponding drop-off`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Memory usage`}),_jsx(_components.td,{children:`> 80% of container limit`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Error rate`}),_jsx(_components.td,{children:`> 1% of requests`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Latency (p99)`}),_jsx(_components.td,{children:`> 500ms`})]})]})]}),`\n`,_jsx(Heading,{level:`3`,id:`backup`,children:`Backup`}),`\n`,_jsx(_components.p,{children:`Relay channel state and signaling presence are ephemeral. Redis persistence is not\nrequired for correctness after a restart, but production configuration must be\nrecoverable. Back up:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Environment variables (including auth tokens)`}),`\n`,_jsx(_components.li,{children:`TLS certificates`}),`\n`,_jsx(_components.li,{children:`Your deployment manifests`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`security-checklist`,children:`Security checklist`}),`\n`,_jsxs(_components.ul,{className:`contains-task-list`,children:[`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Use TLS termination (nginx, Caddy, Cloudflare)`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Use `,_jsx(_components.code,{children:`rediss://`}),`, Redis ACLs, and a private Redis network`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Set channel, client, lifetime-byte, bandwidth, and edge connection quotas`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Run in a DMZ or private network`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Monitor connection counts and error rates`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Set `,_jsx(_components.code,{children:`CROSSLINK_SIGNALING_TOKEN`}),` / `,_jsx(_components.code,{children:`CROSSLINK_RELAY_TOKEN`}),` explicitly`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Enable health checks`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Keep secrets out of source control (env vars, a secrets manager, or Docker/Kubernetes secrets)`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Regularly update the packages`]}),`\n`,_jsxs(_components.li,{className:`task-list-item`,children:[_jsx(_components.input,{type:`checkbox`,disabled:true}),` `,`Restrict network access with firewall rules`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`custom-deployment`,children:`Custom deployment`}),`\n`,_jsx(Heading,{level:`3`,id:`kubernetes`,children:`Kubernetes`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`31`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`31`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`apiVersion`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`apps/v1`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`kind`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`Deployment`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`metadata`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`crosslink-signaling`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`spec`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` replicas`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`3`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` selector`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` matchLabels`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` app`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`crosslink-signaling`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` template`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` metadata`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` labels`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` app`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`crosslink-signaling`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` spec`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` containers`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`signaling`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` image`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`your-registry/crosslink-signaling:latest`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # built per \"Building your own container image\" above`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` ports`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`containerPort`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`8081`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` env`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`PORT`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` value`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"8081\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`CROSSLINK_SIGNALING_TOKEN`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` valueFrom`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` secretKeyRef`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`crosslink-signaling`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` key`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`token`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` resources`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` limits`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` memory`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"256Mi\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` cpu`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"500m\"`})]}),`\n`]})})}),`\n`,_jsx(Heading,{level:`3`,id:`systemd-service`,children:`Systemd service`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`15`,language:`ini`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`ini`,children:_jsxs(_components.code,{language:`ini`,numberOfLines:`15`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:`[Unit]`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`Description`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=Crosslink Signaling Service`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`After`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=network.target`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:`[Service]`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`Type`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=simple`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`User`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=crosslink`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`Environment`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`PORT`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=8081`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`Environment`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`CROSSLINK_SIGNALING_TOKEN`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=change-me`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`ExecStart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=/usr/bin/node /opt/crosslink/signaling/dist/cli.js`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`Restart`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=always`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`RestartSec`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=5`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#D4D4D4`},children:`[Install]`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`WantedBy`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`=multi-user.target`})]}),`\n`]})})})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Self-Hosting","description":"Running your own signaling and relay servers","href":"/guides/self-hosting"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Overview","slug":"overview","depth":2,"children":[]},{"title":"Quick start","slug":"quick-start","depth":2,"children":[]},{"title":"Services","slug":"services","depth":2,"children":[{"title":"Signaling service","slug":"signaling-service","depth":3,"children":[]},{"title":"Relay service","slug":"relay-service","depth":3,"children":[]}]},{"title":"Building your own container image","slug":"building-your-own-container-image","depth":2,"children":[]},{"title":"Environment variables","slug":"environment-variables","depth":2,"children":[{"title":"Signaling","slug":"signaling","depth":3,"children":[]},{"title":"Relay","slug":"relay","depth":3,"children":[]}]},{"title":"Production considerations","slug":"production-considerations","depth":2,"children":[{"title":"TLS termination","slug":"tls-termination","depth":3,"children":[]},{"title":"Load balancing","slug":"load-balancing","depth":3,"children":[]},{"title":"Monitoring","slug":"monitoring","depth":3,"children":[]},{"title":"Backup","slug":"backup","depth":3,"children":[]}]},{"title":"Security checklist","slug":"security-checklist","depth":2,"children":[]},{"title":"Custom deployment","slug":"custom-deployment","depth":2,"children":[{"title":"Kubernetes","slug":"kubernetes","depth":3,"children":[]},{"title":"Systemd service","slug":"systemd-service","depth":3,"children":[]}]}],"codeExamples":{}},"jsLinks":[],"subdomain":"crosslink","siteName":"Crosslink","title":"Self-Hosting - Crosslink","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-08-28T09:28:28.331Z","originalFileLocation":"guides/self-hosting.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Running your own signaling and relay servers\">\u003cmeta data-mint-head name=\"application-name\" content=\"Crosslink\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Self-Hosting - Crosslink\">\u003cmeta data-mint-head property=\"og:description\" content=\"Running your own signaling and relay servers\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Crosslink\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://crosslink.mintlify.app/guides/self-hosting\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Self-Hosting - Crosslink\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Running your own signaling and relay servers\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/crosslink/g_T0MxQ5Qa-ZdsNV/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Crosslink\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#6366f1\">\u003clink data-mint-head rel=\"canonical\" href=\"https://crosslink.mintlify.app/guides/self-hosting\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/guides/self-hosting.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://crosslink.mintlify.app/#organization\",\"name\":\"Crosslink\",\"url\":\"https://crosslink.mintlify.app\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/crosslink/z7NspvKrsX5Mko2W/crosslink.svg?fit=max\u0026auto=format\u0026n=z7NspvKrsX5Mko2W\u0026q=85\u0026s=6ee500820cebb5255a624d81b345c9f1\"}},{\"@type\":\"WebSite\",\"@id\":\"https://crosslink.mintlify.app#website\",\"name\":\"Crosslink\",\"url\":\"https://crosslink.mintlify.app\",\"publisher\":{\"@id\":\"https://crosslink.mintlify.app/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://crosslink.mintlify.app/guides/self-hosting#webpage\",\"url\":\"https://crosslink.mintlify.app/guides/self-hosting\",\"name\":\"Self-Hosting\",\"description\":\"Running your own signaling and relay servers\",\"dateModified\":\"2026-08-28T09:28:28.331Z\",\"isPartOf\":{\"@id\":\"https://crosslink.mintlify.app#website\"},\"breadcrumb\":{\"@id\":\"https://crosslink.mintlify.app/guides/self-hosting#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://crosslink.mintlify.app/guides/self-hosting#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"GUIDES\",\"item\":\"https://crosslink.mintlify.app/guides/react\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Self-Hosting\",\"item\":\"https://crosslink.mintlify.app/guides/self-hosting\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://crosslink.mintlify.app/guides/self-hosting#article\",\"headline\":\"Self-Hosting\",\"name\":\"Self-Hosting\",\"description\":\"Running your own signaling and relay servers\",\"url\":\"https://crosslink.mintlify.app/guides/self-hosting\",\"mainEntityOfPage\":{\"@id\":\"https://crosslink.mintlify.app/guides/self-hosting#webpage\"},\"dateModified\":\"2026-08-28T09:28:28.331Z\",\"publisher\":{\"@id\":\"https://crosslink.mintlify.app/#organization\"},\"isPartOf\":{\"@id\":\"https://crosslink.mintlify.app#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Crosslink\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Crosslink","nav":{"global":null,"groups":[{"group":"GET STARTED","pages":[{"title":"Introduction","href":"/introduction"},{"title":"Quickstart","href":"/quickstart"},{"title":"Installation","href":"/installation"}]},{"group":"BUILD","pages":[{"title":"Building With Crosslink","href":"/build/overview"},{"title":"Your First App","href":"/build/first-app"},{"title":"Capabilities and RPC","href":"/build/capabilities-and-rpc"},{"title":"Local Development","href":"/build/local-development"},{"title":"Production Checklist","href":"/build/production-checklist"}]},{"group":"CONCEPTS","pages":[{"title":"Architecture","href":"/concepts/architecture"},{"title":"Hosts and Clients","href":"/concepts/hosts-and-clients"},{"title":"Links","href":"/concepts/links"},{"title":"Sessions","href":"/concepts/sessions"},{"title":"Group sessions","href":"/concepts/group-sessions"},{"title":"Pairing","href":"/concepts/pairing"},{"title":"Permissions","href":"/concepts/permissions"}]},{"group":"CLIENT","pages":[{"title":"Pairing Card","href":"/client/pairing-card"},{"title":"Mobile Bootstrap","href":"/client/mobile-bootstrap"},{"title":"PWA and Installation","href":"/client/pwa"},{"title":"Durable Origins","href":"/client/durable-origin"},{"title":"Branding and Theming","href":"/client/branding"},{"title":"Custom UI","href":"/client/custom-ui"}]},{"group":"CONNECTIONS","pages":[{"title":"Connection Lifecycle","href":"/connections/connection-lifecycle"},{"title":"Networking","href":"/connections/networking"},{"title":"Reconnection","href":"/connections/reconnection"},{"title":"Offline Behavior","href":"/connections/offline-behavior"}]},{"group":"SECURITY","pages":[{"title":"Security Overview","href":"/security/overview"},{"title":"Authentication","href":"/security/authentication"},{"title":"Encryption","href":"/security/encryption"},{"title":"Hybrid post-quantum exchange","href":"/security/hybrid-pq"},{"title":"Threat Model","href":"/security/threat-model"},{"title":"Permissions","href":"/security/permissions"}]},{"group":"GUIDES","pages":[{"title":"React Integration","href":"/guides/react"},{"title":"Electron","href":"/guides/electron"},{"title":"Tauri Integration","href":"/guides/tauri"},{"title":"Node.js Integration","description":"Using Crosslink in Node.js applications","href":"/guides/node"},{"title":"Self-Hosting","href":"/guides/self-hosting"},{"title":"Scale-out and regions","description":"Run Redis-backed signaling and quota-enforced relays across regions.","href":"/guides/scale-out"},{"title":"Native protocol SDKs","href":"/guides/native-sdks"},{"title":"Connection Modes","href":"/guides/connection-modes"},{"title":"Remote Access","href":"/guides/remote-access"},{"title":"Security \u0026 Encryption Architecture","href":"/guides/security"}]},{"group":"REFERENCE","pages":[{"title":"Package Map","href":"/reference/packages"},{"title":"API Reference","href":"/reference/sdk-api"},{"title":"Configuration","href":"/reference/configuration"},{"title":"Events","href":"/reference/events"},{"title":"Error Codes","href":"/reference/errors"},{"title":"CLI Reference","href":"/reference/cli"},{"title":"Protocol","href":"/reference/protocol"},{"title":"Protocol conformance","href":"/reference/conformance"}]},{"group":"RESOURCES","pages":[{"title":"Examples","href":"/resources/examples"},{"title":"Debug mobile reconnection","href":"/resources/debugging-mobile-reconnect"},{"title":"Troubleshooting","href":"/resources/troubleshooting"},{"title":"FAQ","href":"/resources/faq"}]}]},"title":"Self-Hosting - Crosslink","buildId":"dpl_CUnGQP1HgFUzoxatVC37y4zP83Vx","deploymentId":"dpl_CUnGQP1HgFUzoxatVC37y4zP83Vx","dynamics":[],"chunks":[]}