feat: app-to-app network linking via networkWith description token#1736
Open
Cabecinha84 wants to merge 3 commits into
Open
feat: app-to-app network linking via networkWith description token#1736Cabecinha84 wants to merge 3 commits into
Cabecinha84 wants to merge 3 commits into
Conversation
An app owner can link an app to other apps by embedding a token in the app description text: networkWith:[appA,appB] (brackets required, quotes optional, key case-insensitive, comma separated). This is purely node-local behaviour — no app specification field, no validation change, no network consensus impact. When the token is present: - Before install/redeploy, the node verifies every named app is installed locally and owned by the same owner; otherwise the operation fails. - Each of the app's component containers is attached to the private docker network of every linked app (fluxDockerNetwork_<linked>), so it can reach that app's components by docker DNS name flux<component>_<linkedApp>, as if both apps were a single app. - When a linked-to app is (re)deployed, any locally installed app that is networked with it is reconnected to its network. New module appNetworkLinker.js holds the parser, the install gate, and the forward/reverse network wiring. The gate and forward wiring run in installApplicationHard/installApplicationSoft (the only callers of appDockerCreate), so every container-creation path is covered, including direct callers that bypass registerAppLocally (container health recovery and legacy v<=3 redeploys). Reverse wiring runs in registerAppLocally and softRegisterAppLocally; a boot-time reconcile sweep re-applies all links. dockerService gains an idempotent appDockerNetworkConnect helper. Adds tests/unit/appNetworkLinker.test.js (parser, gate, wiring, reconcile) and appDockerNetworkConnect coverage in dockerService.test.js.
MorningLightMountain713
requested changes
May 22, 2026
Contributor
MorningLightMountain713
left a comment
There was a problem hiding this comment.
Looks good. Small changes
- extract APP_NAME_REGEX (v8+) and APP_NAME_REGEX_LEGACY (v<=7 / components) into appConstants; consume from appValidator and appNetworkLinker - move getAppContainerNames / getAppContainerObjects into dockerService; anchor the multi-component match to ^(?:flux|zel)[a-zA-Z0-9]+_<app>$ and escape regex metacharacters in the app name; refactor getNextAvailableIPForApp to use the same helper - rewrite appDockerNetworkConnect to inspect the container's NetworkSettings.Networks first and skip the connect when already attached; drop the blanket 403 catch (overloaded by docker) in favour of a narrow already-exists message match as a TOCTOU race fallback - update affected unit tests
When a SEND component is being installed in an app whose own compose has no LOG=COLLECT component, walk every app it is networkWith-linked to and ship to the first linked app that exposes a collector. Reachability is provided by the existing networkWith wiring (sender's container is already attached to the linked app's private docker network). Enterprise linked apps whose compose is blanked in the local DB and cannot be decrypted on this node are skipped — the SEND container falls back to json-file logging with a warning. Same fallback applies if the collector container is not reachable at install time. - new appNetworkLinker.findLinkedAppLogCollector(fullAppSpecs) that resolves the linked app + component name (handles the legacy enviromentParameters typo too) - appDockerCreate calls it as a fallback after the existing in-compose collector lookup, only for SEND components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets an app share a private network with other apps and reach their
containers — as if both apps were one. The owner opts in by embedding a
token in the app description text:
Brackets are required, quotes are optional, the
networkWithkey is matchedcase-insensitively, and names are comma-separated. An app whose description
has no (or a malformed) token behaves exactly as before.
This is purely node-local behaviour — no app specification field, no
validation change, no network/consensus impact. The
descriptionfieldalready exists and is signed; the node simply parses it.
A second, related capability rides on top of the same link: a
LOG=SENDcomponent in app B can now ship to a
LOG=COLLECTcomponent in a linkedapp A (previously only same-app sender→collector worked).
Behaviour
When the token is present:
named app is installed locally and owned by the same owner — otherwise
the operation fails.
network of every linked app (
fluxDockerNetwork_<linked>), so it can reachthat app's components by their docker DNS name
flux<component>_<linkedApp>.networked with it is reconnected to its network (auto-heals the gap a hard
redeploy would otherwise leave).
If a linked app is uninstalled, the dependent keeps running and is
automatically reconnected when the linked app returns.
Cross-app log shipping
If a
LOG=SENDcomponent is being installed in an app whose own compose hasno
LOG=COLLECTcomponent, the node walks everynetworkWith-linked app indeclaration order and ships to the first linked app exposing a collector. The
container's Docker GELF log driver is wired to the linked collector's IP at
create time; reachability is provided by the network linking above.
Fallbacks (warning logged, container drops to
json-filelogging):composeblanked in the local DB andcannot be decrypted on this node.
Implementation
appLifecycle/appNetworkLinker.js— token parser, installgate, forward/reverse network wiring, and cross-app log-collector
resolution (
findLinkedAppLogCollector).dockerServiceadditions —appDockerNetworkConnect(idempotentattach via inspect-first),
getAppContainerNames/getAppContainerObjects(anchored regex match for v4+ componentcontainers plus literal match for legacy v≤3 single-component containers;
also consumed by
getNextAvailableIPForAppto eliminate the duplicatedinline filter).
appDockerCreatefalls back tofindLinkedAppLogCollectorforLOG=SENDcomponents when no in-compose collector exists.
installApplicationHard/installApplicationSoft(the only callers ofappDockerCreate), so everycontainer-creation path is covered, including direct callers that bypass
registerAppLocally(container health recovery, legacy v≤3 redeploys). Thegate also runs early in
registerAppLocally/softRegisterAppLocallytofail fast before any side effects.
registerAppLocally/softRegisterAppLocally; aboot-time reconcile sweep in
appStartupManagerre-applies all links.APP_NAME_REGEX,APP_NAME_REGEX_LEGACY)centralised in
utils/appConstants.jsand consumed by bothappValidatorand
appNetworkLinker.appUninstaller,appSpawner,appValidator(behaviour) and routes areunchanged.
Testing
tests/unit/appNetworkLinker.test.js— 30 cases (parser edge cases,install gate, forward/reverse wiring, boot reconcile, cross-app log
collector resolution including the legacy
enviromentParameterstypo andblanked-compose enterprise fallback).
tests/unit/dockerService.test.js— 6 cases forappDockerNetworkConnect(inspect-first / already-attached / inspect-fails / race-window /
generic-404 / overloaded-403) plus 2 cases for
getAppContainerNames(multi+legacy match anchored to flux/zel; regex-metachar escape).
tests in the touched modules.
Notes for reviewers
passive: an app simply fails to install on nodes lacking its linked apps
and the spawn loop retries elsewhere.
networkWith:[A]; Adeclares nothing), but the resulting reachability is mutual.
172.23.0.0/16range alreadypermitted by the
DOCKER-USERfirewall rules.guarantees from the network link it rides on. The linked collector must be
running when the sender installs; if it isn't, the sender silently drops
to
json-file(a warning is logged) and there is no auto-rewire when thecollector later starts. Acceptable for v1; a reactive rewire can follow.