mirror of
https://github.com/logos-co/logos-view-module-runtime.git
synced 2026-08-27 11:01:13 +00:00
* feat(ui-host): adopt the parent's credential through the shared verb
ui-host is handed its parent's per-spawn credential on stdin and installs
it under both bootstrap keys. That was already the right shape — it is the
one place in the system that seeded a store with its OWN identity's
credential rather than the host's — but it spelled the bootstrap key set
out itself, making it the fifth such site.
It now goes through logos::adoptConsumerCredential, so
TokenManager::bootstrapKeys() owns the set. No behaviour change here: this
image's store IS the process ring, which is exactly the case that verb is
narrowed to.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(lock): logos-plugin-qt -> master for logos_consumer.h, protocol to the rev master implements
WHAT MOVED
logos-plugin-qt ef11c210 -> 7cad5ec3 (master, #27 merged)
logos-protocol 79894727 -> b37a2e9f (0.5.0 -> 0.7.0)
WHY plugin-qt HAD TO MOVE
This branch's ui-host calls logos::adoptConsumerCredential and the bridge
takes a logos::ConsumerIdentity, both declared in cpp/logos_consumer.h. That
header did not exist at ef11c210, so every build here died at
LogosQmlBridge.h:17:10: fatal error: logos_consumer.h: No such file or directory
logos-plugin-qt#27 ("logos::admitConsumer -- one home for admitting a
non-module") merged as 7cad5ec3 and ships logos_consumer.h / .cpp. Relocking
that input is the whole fix for the reported red.
WHY protocol MOVED TO 0.7.0 AND NOT TO MASTER
logos-plugin-qt's logos-protocol input `follows` OURS, so this repo -- not
plugin-qt's own lock -- decides which protocol logos-qt-host is compiled
against. The first attempt took protocol to master (42460e5b, 0.8.0). It does
not build, and it does not fail in this repo:
logos-plugin-qt/cpp/logos_consumer.h:68:4: error: #error "logos-protocol is
newer than the consumer-admission contract this file implements. A private
token store is created empty; if the protocol changed how a consumer is
seeded, this file and the hosts calling logos::admitConsumer must move in the
SAME wave. Review adoptCredentialFor / bootstrapKeys, then raise this bound."
That is a deliberate upper bound in plugin-qt master:
#if defined(LOGOS_PROTOCOL_VERSION_MINOR) \
&& (LOGOS_PROTOCOL_VERSION_MAJOR > 0 \
|| (LOGOS_PROTOCOL_VERSION_MAJOR == 0 && LOGOS_PROTOCOL_VERSION_MINOR > 7))
Every other version guard in the fleet is a `>=` floor; this one is a ceiling,
written precisely so a protocol bump cannot outrun the consumer-admission
contract silently. protocol 42460e5b (0.8.0, the INBOUND/OUTBOUND direction
split) merged at 14:16:33Z and logos-plugin-qt#27 merged at 14:16:41Z, eight
seconds later, on top of 0.7 -- plugin-qt master's own flake.lock pins
logos-protocol b37a2e9f. So plugin-qt master implements the <= 0.7 contract and
no branch anywhere raises the bound yet.
b37a2e9f is therefore not a compromise, it is the rev plugin-qt master itself
locks: this closure is the one plugin-qt's own CI validates, and there is no
protocol split. Verified in the built closure:
logos-view-module-runtime compiles against logos-protocol 0.7.0
logos-qt-host-0.1.0 links logos-protocol-lib-0.7.0
logos-cpp-sdk contributes no protocol path at all
(header-only Qt-free types; its own 0.2.0
logos-protocol lock node never reaches the build)
Nothing in this repo references a 0.8-only symbol (no saveInboundToken, no
adoptCredential/adoptCredentialFor, no LOGOS_PROTOCOL_VERSION guard), so 0.8
buys this branch nothing today. flake.nix is untouched and the lock's
`original` for logos-protocol stays master-tracking -- once plugin-qt raises
the bound, a plain `nix flake lock --update-input logos-protocol` carries this
repo to 0.8 with no edit.
CHECKS -- every target the flake exposes for this builder, built individually
with --print-out-paths, upstream cache only (cache.nix.logos.co is 502), so
these are real from-source builds:
packages.x86_64-linux.default OK
/nix/store/gajjq3050m46sprbiqqz0c32mhi95vb7-logos-view-module-runtime-1.0.0
checks.x86_64-linux.default OK
/nix/store/9a4wvnp6b226s2lam54dgxmzm86r9j58-logos-view-module-runtime-check-1.0.0
packages.x86_64-linux.tests OK (same derivation as the check)
/nix/store/9a4wvnp6b226s2lam54dgxmzm86r9j58-logos-view-module-runtime-check-1.0.0
checks.x86_64-windows.default OK (cross to mingw; compile only, nix
forces doCheck off for a cross build,
so no ctest ran there)
/nix/store/csp4n8hn0xrf2j2lxflv6k8affbn180v-logos-view-module-runtime-check-x86_64-w64-mingw32-1.0.0
packages.x86_64-windows.default OK (bin/ui-host.exe is a real PE32+)
/nix/store/s8y4s43nfl8932hnffg645bzn9dqy5fs-logos-view-module-runtime-x86_64-w64-mingw32-1.0.0
checks.x86_64-linux.default runs the suite in its checkPhase:
Start 1: LogosQmlBridgeUnitTests Start 6: BridgeIdentityTests
Start 2: LogosQmlBridgeE2ETests Start 7: LogosQmlBridgeDeferredTests
Start 3: LogosQmlBridgeGuiTests Start 8: LogosQmlBridgeCallsTests
Start 4: LogosQmlBridgeResultTests Start 9: LogosQmlBridgeReplayTests
Start 5: LogosQmlBridgeHandshakeTests Start 10: UiHostUnloadTests
100% tests passed, 0 tests failed out of 10
Total Test time (real) = 39.36 sec
BridgeIdentityTests is the suite this branch adds, so admitConsumer /
adoptConsumerCredential are exercised, not merely linked.
NOT VERIFIED: the aarch64-* and *-darwin outputs. This builder is x86_64-linux
and there is no darwin remote; CI's macos-latest leg covers them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(lock): protocol 0.8 + plugin-qt master, and un-collapse the fixture's two token stores
WHAT MOVED
logos-protocol b37a2e9f (0.7.0) -> 42460e5b (0.8.0, "separate INBOUND from OUTBOUND")
logos-plugin-qt 7cad5ec3 -> 048152f2 (plugin-qt#26)
Six lines of flake.lock; the lock stays at 14 nodes. logos-plugin-qt's
logos-protocol input `follows` OURS, so this repo decides which protocol
logos-qt-host is compiled against, and both halves have to move together.
THIS SUPERSEDES e370ce5's REASONING, WHICH WAS CORRECT WHEN WRITTEN
e370ce5 took protocol to 0.7 rather than master and gave the reason: plugin-qt
master carried a CEILING, not a floor, in cpp/logos_consumer.h --
#if ... || (LOGOS_PROTOCOL_VERSION_MAJOR == 0 && LOGOS_PROTOCOL_VERSION_MINOR > 7))
#error "logos-protocol is newer than the consumer-admission contract this
file implements ... Review adoptCredentialFor / bootstrapKeys, then
raise this bound."
-- and no branch anywhere had raised it. logos-plugin-qt#26 has since merged as
048152f2 and performed exactly the review that text demands. Read from GitHub at
both revs rather than inferred:
7cad5ec3 cpp/logos_consumer.h:67 ... VERSION_MINOR > 7))
048152f2 cpp/logos_consumer.h:90 ... VERSION_MINOR > 8))
#26 also makes 0.8 mandatory rather than merely permitted: cpp/logos_provider_
object.cpp and cpp/qt_provider_object.cpp call TokenManager::saveInboundToken
unguarded, which does not exist before 0.8. plugin-qt master and protocol 0.8
are now one step, and 0.7 is no longer reachable from this branch.
Verified in the built closure rather than in the lock -- the version literal in
the header that actually reached the compiler, on both targets:
LOGOS_PROTOCOL_VERSION_MAJOR 0 / LOGOS_PROTOCOL_VERSION_MINOR 8
and exactly ONE logos-qt-host and ONE logos-protocol derivation in the .drv
graph of every output (the runtime closure cannot see either -- logos_qt_host is
linked statically -- so this was read from `nix-store -qR` over the DERIVATION,
which is the instrument that finds a split host):
x86_64-linux logos-qt-host-0.1.0.drv x1
logos-protocol{,-lib,-headers}-0.8.0.drv one derivation
x86_64-windows logos-qt-host-x86_64-w64-mingw32-0.1.0.drv x1
logos-protocol-*-x86_64-w64-mingw32-0.8.0.drv one derivation
── THE ONE TEST THAT MOVED, AND WHY IT IS THE FIXTURE AND NOT THE PRODUCT ─────
At 0.8 this branch built clean and ran 10/11, with case 1 red:
FAIL! : TestBridgeIdentity::aHostIdentityBridgeReachesAnUndeclaredBackend
WithNoHandshake() '!payloadIsError(payload)' returned FALSE.
({"error":"Module source unavailable",
"message":"call to 'backend_module' rejected: token not recognized
(re-exchange failed)"})
Case 1 is the deliberately-GREEN ambient-ring escalation control, and its own
comment says that if it goes red the ambient ring changed shape and every
conclusion in the file needs re-deriving. So this had to be decided, not
silenced. Two readings were live:
(A) the fixture seeds the ring with saveToken -- the OUTBOUND door -- and 0.8
authorizes against the INBOUND view, so the fixture stopped modelling
production and the TEST should change;
(B) any host that seeds a peer's token with saveToken loses in-process
authorization at 0.8, and the PRODUCT should change.
It is (A), but NOT for the reason (A) states, and the difference decides what
the replacement asserts. The fixture's ambient seed is still a faithful model of
logos-liblogos/src/logos_core/module_manager.cpp:336. What the fixture got wrong
is that it COLLAPSED TWO STORES INTO ONE: both ModuleProxys took the defaulted
store, so the "module's" credential was whatever the host's ambient ring held --
the capability bootstrap kCapToken -- instead of the module's own root token.
In production those are two objects in two images, holding ONE value:
logos-liblogos/src/logos_core/module_manager.cpp:329-336
the HOST mints authToken, sendToken()s it to the module, and caches it
OUTBOUND under the module's name.
logos-module-loader-qt/src/host/module_initializer.cpp:169-170
the MODULE's image writes that same value under both bootstrapKeys(),
which is what TokenManager::credential() is derived from.
0.8 closed the OUTBOUND-MAP route into a provider. It left the ANCHOR route
open, deliberately, and logos-protocol pins that at master in
tests/protocol/test_token_direction.cpp:221
(TokenDirection.TheOwnCredentialStillAuthorizesAndStillGatesPushes): a module
authorizes anyone presenting ITS OWN credential. Since the ambient ring's entry
for a module IS that module's credential, the escalation case 1 records is STILL
LIVE at 0.8 -- which is why the fix is to give the backend the store a real
module has, and emphatically NOT to make the call fail.
THE REMEDY NOT TAKEN. Routing the seed through informModuleToken /
saveInboundToken -- reading (A) as literally stated -- files a caller GRANT.
Case 1 would then assert "a caller that was granted may call": trivially true,
green forever, and blind to the escalation the file exists to record. That is
worse than red, and it is the trap this fleet keeps hitting.
WHAT CHANGED IN tests/test_bridge_identity.cpp
* backendImageStore() -- the backend module gets its OWN TokenManager
(isolateIdentity + forIdentity + adoptCredential(kBackendRootToken)), the
same three steps logos-protocol's own ModuleImage helper uses, and
backendProxy is constructed with the 3-arg ModuleProxy ctor against it.
* the ambient-ring seed is UNCHANGED -- it was never the thing that was wrong.
* capability_module DELIBERATELY stays on the ambient ring: its credential is
the host's kCapToken, which is what lets logos::admitConsumer's
informModuleToken push clear the trusted-channel gate. Isolating it too
breaks cases 3-8 for reasons unrelated to any of them.
* case 1 now pins the MECHANISM, not only the outcome:
QCOMPARE(TokenManager::instance().getToken("backend_module"),
fx.backendStore->credential()); // one secret, two stores
QVERIFY(fx.backendStore->inbound().keys().isEmpty()); // nothing granted
plus a null/identity check on the store, because a null would have silently
defaulted the proxy back onto the ambient ring and made the whole case
theatre.
── THE CONTROLS. A CONTROL THAT CANNOT FAIL IS NOT A CONTROL ─────────────────
Case 1 asserts that an undeclared backend IS reachable, so "reachable" is its
GREEN state and it cannot be made red by making the backend reachable. What CAN
be shown -- and was, by mutating one thing at a time in the built tree and
re-running -- is that it goes red on every way the ambient ring's shape can
change, and that the tautology substitution is blocked. Five mutations, five
reds, each in a different assertion:
NULL unmutated 11 passed, 0 failed
A ring caches a value no image holds 9/2 -- QCOMPARE (mechanism 1)
(module_manager.cpp:336 changes shape) + case 5's ring assert
B the two stores collapsed back into one 10/1 -- '!payloadIsError'
(literally today's fixture) with the exact 0.8
"token not recognized
(re-exchange failed)"
C target INFORMED of the caller instead 10/1 -- inbound().keys()
(the tautology substitution; the call .isEmpty()
still succeeds, and the test refuses
to call that a pass)
D module image never adopts a credential 10/1 -- QCOMPARE (mechanism 1)
(module_initializer.cpp:169-170 changes)
E case 5's admitted consumer is HANDED the 10/1 -- 'payloadIsError'
ambient entry, so an undeclared backend ("undeclared backend
IS reachable with no handshake was REACHED: hello")
E is the literal "make it reachable, show RED", answered by the assertion that
owns that claim: case 5 is the polarity case 1 is not. The file carries both, so
it cannot pass vacuously in either direction.
── PRODUCT: NOTHING TO CHANGE HERE ───────────────────────────────────────────
This repo has no saveToken call in production source at all. ui-host/main.cpp:221
adopts its OWN per-spawn credential through logos::adoptConsumerCredential -- the
value the parent minted FOR IT, not a peer's token through the outbound door --
which is the correct direction and is what this branch already did. The one
shipped (B) in the fleet is logos-logoscore-cli's daemon
(src/daemon/daemon.cpp:629, saveToken("cli_client", ...)), fixed separately in
logoscore-cli#104.
── CHECKS, EVERY OUTPUT INDIVIDUALLY, --print-out-paths ──────────────────────
packages.x86_64-linux.default 34vfy1yshm3hqk06ngsl3dycfwqlqssj-…-1.0.0
packages.x86_64-linux.tests 38md8bjl93ga42z7d94nxrqjqq8qldx6-…-check-1.0.0
checks.x86_64-linux.default 38md8bjl93ga42z7d94nxrqjqq8qldx6-…-check-1.0.0
packages.x86_64-windows.default kyncplgbrryb40sa8p8dg5037k3qrslq-…-mingw32-1.0.0
packages.x86_64-windows.tests jdpgmgi4w7qaxhxzhl5s0kpixbncj7j6-…-check-mingw32
checks.x86_64-windows.default jdpgmgi4w7qaxhxzhl5s0kpixbncj7j6-…-check-mingw32
checks.x86_64-linux.default runs the suite in its checkPhase:
1/10 LogosQmlBridgeUnitTests 6/10 BridgeIdentityTests Passed
2/10 LogosQmlBridgeResultTests 7/10 LogosQmlBridgeGuiTests
3/10 LogosQmlBridgeE2ETests 8/10 LogosQmlBridgeDeferredTests
4/10 LogosQmlBridgeReplayTests 9/10 UiHostUnloadTests
5/10 LogosQmlBridgeHandshakeTests 10/10 LogosQmlBridgeCallsTests
100% tests passed, 0 tests failed out of 10 (38.96 sec)
BridgeIdentityTests itself: 11 passed, 0 failed, 0 skipped. The cross target
compiles tests/test_bridge_identity.exe but nix forces doCheck off for a cross
build, so no ctest ran there.
NOT BUILT: aarch64-linux and both darwin. This builder is x86_64-linux and there
is no darwin remote; all nine of those outputs EVALUATE (drvPath resolved for
packages.default, packages.tests and checks.default on each), which is the part
that catches the pseudo-system eval traps. CI's macos-latest leg covers the rest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>