Architecture ADRs: the client's future shape, runtime, identity, and API - #149
Open
darksidemilk wants to merge 5 commits into
Open
Architecture ADRs: the client's future shape, runtime, identity, and API#149darksidemilk wants to merge 5 commits into
darksidemilk wants to merge 5 commits into
Conversation
…ntity fog-client has had no functional commit since March 2023 while the server it talks to has kept moving. These three ADRs record an exploration of what to do about that. Nothing is implemented; ADR-0001 is deliberately left open for the maintainers to settle. The number the exploration turned on: 3,013 lines under Modules/ do the actual work, while 5,658 lines across UniversalInstaller, SetupHelper, UpdateHelper, UpdateWaiter, Updater and Bus exist only to ship those features as a self-updating compiled binary with its own IPC bus. Both figures are reproducible with the commands quoted in ADR-0001. 0001 — How much of FOG belongs on the endpoint. Open, no recommendation. Lays out deploy-time-plus-signed-scripts against keeping a compiled agent. Does settle two things: agentless push is rejected in both futures (the server would have to store reusable fleet credentials, it cannot route to roaming clients, and WinRM/schtasks need a GPO or an agent to enable the thing meant to replace the agent), and a "thin agent" is a ratchet rather than a compromise. Notes that the rename module is already agentless in production via FOS's changeHostname(). 0002 — The runtime, if a compiled agent survives. Conditional on 0001. Recommends the current .NET LTS: only five language constructs actually block the port, and the per-OS seam the platform packages need already exists. Rejects Go (a rewrite from here), Rust (maintainer pool), Node (posture for a SYSTEM service) and Electron (a service has no window). States the payload regression as an estimate to be measured, not a fact. 0003 — Client identity belongs in the application layer, not the certificate chain. Independent of 0001, so it can be settled first. Recommends a per-client Ed25519 keypair plus ordinary platform TLS over per-client X.509/mTLS, which would reintroduce the online CA key that GHSA-94p8-jg9j-99v4 caused to be taken offline. Covers migration with no machine touches, and the imaging problem at length — a credential on disk gets cloned by the product's own core function. Also corrects three claims about this codebase that are widely repeated and wrong: AD join is a netapi32 P/Invoke rather than System.DirectoryServices, there is no WMI inventory in the client, and printer management is mostly shell-outs. Each has been used to argue for a conclusion it does not support. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hejbUjsNJ7PyMdh8Cpet7
The first version wrote Wall 1 from the 1.5 line, where a commented-out sysuuid path stood as evidence that hardware identifiers had been tried and abandoned. That is no longer the state of the branch this ADR targets. fogproject ADR-0039 (dated 2026-09-02) landed SmbiosIdentity on working-1.6: four SMBIOS fields scored in order, a placeholder table from hardware seen in the wild, and a repeated-character rule that handles the all-F MSI UUID which sank the 2018 attempt. So the "duplicate UUIDs make this unusable" objection does not hold here and this ADR should not repeat it. The wall stands for a different and better reason: 0039 deliberately scoped itself to the boot path, keeping the MAC as the identity, and says outright that the FOG client needs no change. It solved resolution -- which host is this. It did not touch authentication, and cannot, because every value SmbiosIdentity reads is a string the caller supplies. Identification is not authentication. That also makes the imaging section's binding defence cheaper without reopening what 0039 settled: the signature decides, and the firmware fingerprint is only a consistency check on a credential that already proved itself. SmbiosIdentity is reused rather than duplicated because it already encodes which firmware values are worthless. Other changes: - Status now says working-1.6 only, and that the 1.5 line is out of scope rather than merely unmentioned. Server paths are 1.6 paths (packages/web/src/, not lib/fog/*.class.php). - References fogproject ADR-0043 (the server half), 0039, 0027 (the same plaintext-credential argument one layer up) and 0036/0037/0040 (the PKI direction that rejecting per-client X.509 keeps faith with). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hejbUjsNJ7PyMdh8Cpet7
Same correction as fogproject ADR 0043. The page does not simply assert the stale claim -- its "How FOG uses certificates" section opens with a callout that already states the correct position, that the zones are independent and the web certificate can be replaced without disturbing fog-client. What is stale is the body under that callout and the TL;DR at the top, which still name fog-client as the constraint and recommend step-ca. An internal contradiction is a different defect from a wrong page, and the ADR should describe the one that is actually there. Also names both copies explicitly (fogproject's docs/EXTERNAL_CA_AND_LETSENCRYPT.md and fog-docs' docs/kb/integrations/external-ca-lets-encrypt.md) since they carry the same text and both need the same fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hejbUjsNJ7PyMdh8Cpet7
ADR-0003 linked a companion record proposed in fogproject as ADR-0043. That proposal has been withdrawn and its PR closed -- the ask here was for ADRs on fog-client, and opening a numbered record in another repository's curated ADR sequence went beyond it. The link is removed rather than repointed, because there is nothing to point at. Nothing else changes: the server-side consequences this ADR describes are part of the client-identity proposal and belong in it, and the remaining fogproject references (0027, 0036, 0037, 0039, 0040) are reading references to records that already exist on working-1.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hejbUjsNJ7PyMdh8Cpet7
FOGProject/fog-sdk was created on 2026-08-29, after the exploration behind 0001-0003, and it changes the arithmetic on a question those three left as hand-written work: how the endpoint talks to the server. FOG 1.6 already serves its own OpenAPI document live from /system/openapi, and fog-sdk already generates PowerShell and Python clients from a pinned snapshot of it. The spec, the generator and the release process all exist. The SDK itself cannot go on endpoints, and the reason is worth recording because "we have an SDK now, use it" is the obvious wrong next step. All 380 paths in the snapshot are the administrative CRUD surface; there is no host check-in surface in the document at all. And all three security schemes authenticate a person -- tokens that act with their owner's roles, carry no scope, never expire, and are revocable only in the UI. Shipping one to a thousand workstations is the exact inverse of what 0003 argues for. So the SDK is not the answer; the pipeline behind it is. The decision is to define the client protocol as real REST operations inside the same document, under a separate hostAuth security scheme, and generate the endpoint's HTTP layer rather than hand-write it. Whatever 0003 settles on defines that scheme. The strongest support came from fogproject's own tests/openapi-route-coverage.test.php, which already enforces both directions of route/document agreement and whose docblock makes this ADR's argument unprompted: a client generated from the document simply has no method for a missing endpoint, and concludes the server cannot do it. The host protocol is the largest body of routes entirely outside that guarantee, so bringing it in puts it under a test that already runs. Independent of 0001 and 0002 -- a generated client is equally useful to a compiled agent and to a signed script -- and a companion to 0003 rather than a dependency of it. Cross-referenced from all three; 0001's status line and 0002's Phase 4 updated to match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016hejbUjsNJ7PyMdh8Cpet7
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.
Exploratory. Nothing is implemented and no code is changed — four Markdown files under
docs/adr/, nothing else. One of the four deliberately carries no recommendation; it is written to be settled here, in review.fog-client has had no functional commit since March 2023 (v0.13.0) while the server it talks to shipped PKI work this year and still pins
FOG_CLIENT_VERSIONat0.13.0. These documents record what turned up when we went looking at why, and what the options actually are.The number the exploration turned on
Nearly twice as much code exists to let the agent be a self-updating compiled binary with its own IPC bus as exists to do the work. Of 17,526 lines across this repo and
zazzles, roughly 3,000 are the product. Every other observation is downstream of that ratio.The four ADRs
0001 — How much of FOG belongs on the endpoint — open, no recommendation. Deploy-time provisioning plus signed scripts on native schedulers, against keeping a compiled agent, with a module-by-module feasibility table. It does settle two things: agentless push is rejected in both futures (the server would have to store reusable fleet credentials on arguably the worst-defended box on the network; it cannot route to roaming or NATed clients; and on non-domain-joined Windows both WinRM and
schtasks /sneed a GPO or an agent to enable the thing meant to replace the agent), and a "thin agent" is a ratchet rather than a compromise — because it is compiled it needs an updater, which needs a second process, which needs a bus. That is not speculation; it is the 5,658 lines above.Worth knowing before reading: the rename module is already agentless in production.
funcs.sh:changeHostname()mounts the deployed NTFS volume and rewrites twenty registry values throughreged. The C# path is a fallback.0002 — The runtime, if a compiled agent survives — conditional on 0001. Recommends the current .NET LTS. Only five language constructs actually block the port (
Thread.Abort,AppDomain.CreateDomain, theRSACryptoServiceProvidercasts,AesCryptoServiceProvider,ServiceBase.Run), and the per-OS seam the Windows-only packages need already exists —System.Managementbeing Windows-only costs nothing when its callers are already inWindows/directories. Rejects Go (not worse, but worse from here), Rust (maintainer pool, said plainly), Node (posture for a SYSTEM service), Electron (a service has no window).0003 — Client identity belongs in the application layer, not the certificate chain — independent of 0001. A per-client Ed25519 keypair plus ordinary platform TLS, over the per-client X.509/mTLS that "issued certs per client" suggests — that would reintroduce an online CA key months after GHSA-94p8-jg9j-99v4 caused one to be taken offline, and its revocation story degrades to a DB allowlist anyway. Covers migration with no machine touches, and the imaging problem at length.
Its Wall 1 was rewritten after finding
fogprojectADR-0039 (2026-09-02), which landedSmbiosIdentityand deliberately kept the MAC as the identity. 0039 solved resolution; this is about authentication, which 0039 left untouched.0004 — The client API is described in FOG's own OpenAPI document — new, independent of all three. Prompted by
FOGProject/fog-sdk, created 2026-08-29, which generates PowerShell and Python clients from a pinned snapshot of FOG's live OpenAPI document.The SDK cannot go on endpoints, and the reason is the useful part: all 380 paths in the snapshot are the administrative CRUD surface — there is no host check-in surface in the document at all — and all three security schemes authenticate a person, with tokens that act with their owner's roles, carry no scope, never expire, and are revocable only in the UI. Shipping one to a thousand workstations is the exact inverse of 0003.
So the SDK isn't the answer; the pipeline behind it is. Define the client protocol as real REST operations in the same document, under a separate
hostAuthscheme, and generate the endpoint's HTTP layer instead of hand-writing it. The strongest support came from this project's owntests/openapi-route-coverage.test.php, whose docblock makes the argument unprompted: "a client generated from the document simply has no method for the missing endpoint, and the reasonable conclusion is that the server cannot do it." The host protocol is the largest body of routes entirely outside that guarantee.Three claims about this codebase that are widely repeated and wrong
Each has been used to argue for a conclusion it does not support:
System.DirectoryServices— it is anetapi32P/Invoke (NetJoinDomain/NetUnjoinDomain). Any runtime with FFI calls it identically, so this is not an argument for .NET.rundll32 printui.dll,lpadmin), which lowers the bar for a non-.NET runtime.And one about the docs: a Let's Encrypt certificate on the vhost does not break fog-client. Both copies of that page are internally inconsistent rather than wrong — the How FOG uses certificates section opens with a callout stating the correct post-zone-split position, while the body and TL;DR still name fog-client as the constraint and recommend step-ca.
A note on scope
0003 mentions a live vulnerability found while writing it and deliberately does not describe it, because this PR is public and the fix is small enough that the diff would disclose it. It's written up separately for the private advisory process in
fogproject'sSECURITY.md. Those fixes should not wait for any decision here.Review
The useful thing to push back on is 0001 — that is the point of it being open. The weakest quantitative claim is 0002's 60–90 MB self-contained payload estimate, flagged in the text as an estimate. The LOC figures reproduce with the commands above.
None of these propose a change to
fogprojectorfog-sdk; references to those repos' ADRs and tests are reading references only.🤖 Generated with Claude Code
https://claude.ai/code/session_016hejbUjsNJ7PyMdh8Cpet7