mirror of
https://github.com/logos-co/logos-liblogos.git
synced 2026-08-27 04:41:12 +00:00
master
183
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
82a17ba13d |
feat: bundle and auto-load modules_state
The observer and the feed merged in #189, but nothing shipped or loaded the module, so in a normal run the whole thing was inert -- correctly, since the feed arms only when modules_state itself loads. BUNDLE. nix/modules.nix took one hardcoded capability_module; it now takes a list of { name; pkg; version; }. Same generated manifest, same three traps its comments encode (the required "type" field, the explicit extension list that Windows needs, target-vs-build platform keys). AUTO-LOAD. initializeModulesState() runs from logos_core_start() after initializeCapabilityModule(). Optional by construction: absent, it returns false, nothing changes, and the observer keeps early-outing with no sink. LOADING LAST IS FINE, and this is the part that is easy to get wrong. The membership edges from discoverInstalledModules and capability_module's own load happen BEFORE any sink exists, so they are dropped. The snapshot back-fills them. modules_state cannot be first, so it must not need to be -- which is exactly what apply_snapshot is for. MEASURED, stock daemon, no -m flag, no test door, no load-module: module list capability_module state:loaded seq:2 modules_state state:ready seq:4 pid:2515 partial false REFUSED 0 snapshot 249 ms after "Module loaded: modules_state", exactly once capability_module appearing at all is the proof the snapshot ran: it loaded before the feed existed, so only apply_snapshot could have put it there. `state:ready` rather than `loaded` exercises the loaded->ready publish transition end to end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f05ed5a899 |
fix(doctests): unpin logoscore-cli, and retire the archived accounts module
The macOS doc-test job has been red since 2026-08-21: every module call came
back {"__logos_rpc_status__":"unauthorized"}, preceded in the daemon log by
capability_module rejecting the CLI's own requestModule handshake. Linux was
green throughout.
NOT MODULE ROT. A freshly-built test_basic_module failed identically to
accounts_module. Bisected on one macOS box, one variable, same CLI, same module:
liblogos
|
||
|
|
2ac002b410 |
fix(core): keep ready across a snapshot
buildSnapshotListing mapped loaded ? kLoaded : kUnloaded and never looked at `published`, so every snapshot reported an already-published module as merely `loaded`. Snapshot records draw fresh seqs from the same counter, so they outrank every earlier transition and win the replay rule -- and the readiness watch is one-shot, so nothing re-emits. A module downgraded this way stays `loaded` for the rest of the session while answering calls normally. Verified against a live daemon by forcing a second snapshot (reload modules_state after test_fullapi_cpp is ready): before this fix: ready -> snapshot -> loaded after: ready -> snapshot -> ready `published` is null when no watch is armed, hence the is_boolean check rather than value(..., false): unknown readiness must report `loaded`, never `ready`. Not covered by a unit test: buildSnapshotListing is file-local and the 194 existing tests never reach it -- they install no sink, so nothing arms a watch. The check above is a live-daemon A/B. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
84564f0f6a |
feat(core): turn module lifecycle into sequenced facts, and feed them to modules_state (#189)
* feat(core): observe module lifecycle as sequenced facts, not log lines
Until now load, unload and crash were spdlog::info lines and registry
membership changes were silent, so every consumer polled — basecamp runs a
2s QTimer and infers module state from package-install events.
ModuleStateObserver turns those into structured, sequenced transitions and
hands them to a SINK. It reports only: it does not talk to any module and
does not know modules_state exists. Wiring a sink that pushes to that module
is the next stage. With no sink installed record() early-outs before it
allocates, so a host consuming nothing pays nothing — and buffering with no
consumer would be an unbounded leak in the normal case.
TWO RULES, both load-bearing:
1. Never dispatch under loadMutex(). record() buffers; flush() dispatches,
and every entry point declares ScopedModuleStateFlush BEFORE its lock
guard so it is destroyed AFTER it. A sink doing an RPC from inside the
load path while holding that lock is the shape of two failures already
paid for here: the ui-host startup token deadlock, and the ~417s basecamp
stall from a synchronous call to an absent module.
2. One seq counter, for deltas and snapshots alike. Consumers apply a
transition only when its seq beats what they hold for that module, and
keep a seq tombstone for a departed one. A second counter makes that
tombstone either unreachably high (a real later delta dropped forever) or
trivially low (a stale delta resurrecting a pruned module).
Seams: unloaded->loading at load start, loading->loaded on success carrying
instanceId and pid, loading->error on all three failure paths,
loaded->stopping->unloaded on unload, and the membership edges
absent->unloaded / unloaded->absent in discovery and prune. processModule()
gets the discovery edge too — it is a second way a module enters the
registry, and a consumer that only saw scan edges would be surprised by a
`unloaded -> loading` for a module it had never heard of.
Two bugs found while wiring it, both of which would have made the feed lie:
* onTerminated fires for BOTH an orderly unload and a module that died,
and cannot tell them apart from its arguments. Teardown now announces
intent before terminate(); the callback consumes it, once, so an
unload/reload/crash still reads as a crash.
* terminateAll() and clear() tear down every loaded module at once, so a
CLEAN HOST SHUTDOWN would have reported the entire fleet as crashed —
`loaded -> error`, "module exited without being asked to", once per
module. They now announce every loaded module first.
9 tests, and they are proven to bite: making record() dispatch inline (the
rule-1 violation) fails RecordDoesNotDispatch and reddens the check. Full
suite 194/194.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(core): feed module lifecycle to modules_state
The consumer end of ModuleStateObserver. The observer produces sequenced
transitions and knows nothing about any module; this turns them into calls on
`modules_state`, so the state liblogos has always had stops being spdlog lines
and becomes queryable and subscribable.
Modelled on the capability_module push already in this file -- one long-lived
"core" LogosAPI, per-module transport honoured -- with three differences, all
forced by where it runs:
1. ASYNC deltas. registerRestrictionRpc is synchronous and gets away with it
because it is rare and short. This runs on EVERY load, unload and crash,
from the observer's flush, on whichever thread did the work. A synchronous
RPC there would put a 20 s worst case on the load path.
2. CHEAP NO-OP WHEN ABSENT, checked before the client is even fetched. A
synchronous dial to a module that is not there cost Basecamp ~417 s of
blocked GUI thread once already.
3. THE SINK IS UNINSTALLED when modules_state unloads, so the observer goes
back to buffering nothing rather than buffering into a sink that drops.
SNAPSHOT ON AVAILABILITY, NOT ON LOAD. modules_state loads after other modules,
so deltas alone give it a permanently short list -- which is what `partial`
exists to signal. The snapshot clears it. It is armed with whenObjectAvailable()
rather than fired at load, because `load-module` returns when the plugin is IN
and the module PUBLISHES later -- measured elsewhere at ~390 ms on a cold start
-- and whenObjectAvailable is the primitive that waits without failing fast or
burning the acquire timeout on the calling thread.
ONE SEQ COUNTER. Every record seq and the listing seq come from the observer's
counter, listing drawn LAST so it is >= every record in it. modules_state
tombstones a pruned record at the LISTING's seq, so a second counter would make
that tombstone either unreachably high (a real later delta dropped forever) or
trivially low (a stale delta resurrecting a pruned module).
partial:false is a claim, and it is defensible: `partial` means the host's scan
SKIPPED something, and discoverInstalledModules drops a module it cannot read
before it ever enters the registry. Anything missing is not something the host
knows and is withholding; it is something the host does not know.
PROVEN END TO END against a live daemon, with NO test door open -- so the facts
arriving also prove the module's structural gate admitted core as kind=host:
snapshot: "Pushed module snapshot to modules_state", and list_modules then
reports both modules with real paths and load timestamps,
partial:false, seqs 1/2 under listing seq 3.
delta: unload capability_module -> its record goes loaded(seq 2) ->
unloaded(seq 5) through the ASYNC path, which is separate code from
the snapshot's synchronous one and needed proving separately.
refusals: 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor: cut the comment walls
Same pass as the module side. The prose had grown past the point where it helps.
module_state_observer.h 174 -> 127 lines, 103 -> 56 comment
module_manager.cpp 285 -> ~215 comment
module_registry.cpp / observer.cpp / tests / README trimmed to match
Kept the non-obvious why: the two rules (never dispatch under loadMutex, one seq
counter for deltas and snapshots), orderly-teardown vs death, why a clean
shutdown would otherwise report the fleet as crashed, and why partial:false is a
defensible claim rather than an assumption.
The worst offender was mechanical: the four-line "declare the flusher before the
lock guard" explanation was pasted at all SEVEN call sites. It is now one line
pointing at the rule in the header, where the explanation lives once.
194/194.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(core): emit loaded -> ready when a module publishes
`loaded` means the host owns the process: markLoaded runs once the child is
spawned and its token is written, which is strictly before the module publishes
its object (~390ms cold for modules_state; 826ms measured for a module with an
empty initializer). Nothing emitted that gap, so consumers had to treat "loaded"
as "callable" and be wrong for the width of the window.
Adds a second edge rather than moving the first. `loaded` stays an ownership
fact -- total across loaders, and the thing unload and the double-spawn guard
need. `ready` is the readiness fact, observed asynchronously.
armReadinessWatch arms a one-shot whenObjectAvailable and returns: it never
waits, so rule 1 (no dispatch under loadMutex) holds, and the callback lands
with no lock held. Only armed when a sink is installed -- without a consumer
each watch would hold a client and a replica for nothing, and it keeps the
FakeModuleLoader tests, which publish nothing, arming nothing.
The callback carries the loadEpoch it was armed under and markPublished drops
it if that no longer matches, so a fast unload/reload cannot let a stale watch
mark the new instance ready. Epoch rather than loadedAt: the latter is whole
seconds and collides.
modules_info gains `published` / `published_at`. published is null, not false,
when no watch is armed -- "nobody looked" and "not ready" are different answers.
Also folds capabilityModuleClient and modulesStateClient into one
moduleClient(name); they were identical apart from the name.
checks.tests green: 194 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
959d11d933 |
chore(deps): protocol 303ab08 across all three carriers
Level 3 of the propagation of logos-protocol#74 to logos-logoscore-cli, after
logos-plugin-qt#30 and logos-qt-sdk#46. Lock-only.
THREE inputs are bumped, not one, and that is the point of this level:
logos-protocol, logos-qt-sdk and logos-plugin-qt. Those are exactly the inputs
that CARRY a runtime liblogos_protocol.dylib into this closure. Measured across
all eight non-nix inputs -- cpp-sdk, module-loader, capability-module, module
and package-manager carry none.
This repo declares `inputs.logos-protocol.follows` on logos-cpp-sdk, which is
the one input that does NOT carry a protocol lib, and omits it on qt-sdk and
plugin-qt, which do. So their protocol rev is whatever their own locks say, and
each has to be bumped by hand. Today that is invisible because every rev
coincided; the moment one moves alone this closure holds two protocol libs,
which is the duplicate-runtime condition the one-runtime symbol gate exists to
catch. Worth a follow-up (two `follows` lines) rather than repeating this by
hand next time -- deliberately NOT folded in here, so a red artifact could not
be ambiguous between the bump and a topology change.
THE LOCK STILL SHOWS FOUR PROTOCOL REVS AFTERWARDS AND THAT IS CORRECT. 7 nodes,
4 revs: 303ab08 x3 (the carriers), 42460e5 x2 under default-module-loader and
logos-capability-module, plus two deeper. Lock-node count and closure
composition are different questions, and only the second one ships:
protocol paths in the built logos-liblogos closure: exactly ONE
n7yilrs2...-logos-protocol-lib-0.8.0 (the build of 303ab08)
which is the same store path verified in the two levels below to carry the
change (UTF-16 "(any)" x1, old warning x0) where the lib it replaces does not.
`nix flake check`: exit 0, 2 checks, no failures. Status read from nix rather
than from a pipeline -- `nix flake check | tail` reports tail's exit code, which
is always 0, and that misreported a check earlier in this chain.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
7234c26845 | chore(deps): relock module loader stack | ||
|
|
6be8eb0c01 |
chore(deps): move onto logos-protocol 0.8 and logos-plugin-qt master
logos-plugin-qt#26 makes protocol 0.8 a HARD FLOOR for every consumer of
logos-qt-host: cpp/logos_provider_object.cpp and cpp/qt_provider_object.cpp call
TokenManager::saveInboundToken unguarded, so the two inputs move together or not
at all. liblogos links logos-qt-host directly, and it is upstream of both apps
(each nix/app.nix copies liblogos's lib/* over the app), so it goes first.
logos-protocol 2e3344ac -> 42460e5b (0.7 -> 0.8, protocol#73)
logos-plugin-qt 1aa3e31c -> 048152f2 (plugin-qt#26)
NO CODE CHANGES, and that was checked rather than assumed. 0.8 changes which
store ModuleProxy::authorize reads, so every token site here was classified:
module_manager.cpp:336 saveToken(name, authToken) OUTBOUND, still correct
module_manager.cpp:132 getToken("capability_module") outbound read
module_manager.cpp:219 getToken("capability_module") outbound read
logos_core.cpp:82 getToken(key) outbound read
module_manager.cpp:223 client->informModuleToken(...) inbound grant, over the
wire -- 0.8 routes it through saveInboundToken on the
RECEIVING side, which is a different process here
:336 is the host caching a module's OWN host-issued credential under that
module's name; protocol pins that meaning in
TokenDirection.TheOwnCredentialStillAuthorizesAndStillGatesPushes. liblogos is
therefore NOT an instance of the "seeded a peer's token through the outbound
door, then expected to authorize an inbound call against it" defect -- it
authorizes no inbound call at all: it registers no provider anywhere in src/,
and its default loader puts every target in another process.
Verified by BUILDING all ten outputs individually with --print-out-paths, on a
24-core x86_64-linux box:
x86_64-linux lib bin include modules tests default portable all OK
checks.tests 185 tests, 0 failures, 0 errors
x86_64-windows default portable all OK
Closure audit (nix-store -qR on packages.<sys>.default), which is the thing this
whole layer exists to keep at one:
x86_64-linux 1 logos-qt-host (ka5vgzb8…)
1 logos-protocol derivation at 0.8, 3 outputs
(…-lib-0.8.0, …-headers-0.8.0, and the symlinkJoin)
x86_64-windows 1 logos-qt-host (cksg9kw6…-x86_64-w64-mingw32)
1 logos-protocol-lib-x86_64-w64-mingw32-0.8.0
liblogos-bin's lib/liblogos_qt_host.so is a re-export COPY of the input's and
compares byte-identical to it, so the two paths are one image. logos-qt-sdk no
longer ships a qt-host of its own; it only propagates that one.
Not fixed here, and now documented at the logos-capability-module input with the
measurement: the capability_module this repo SHIPS is built by its own
logos-module-builder chain against logos-protocol 0.4.0 and a pre-#26
logos-qt-host, so the repo really does emit two logos-qt-host images -- invisible
to nix-store -qR because the plugin's references are scrubbed. The five-line
`follows` that closes it was written and measured (lock 211 -> 174, symbols
58 -> 71, Linux fully green) and then REJECTED: it makes capability_module the
first module on plugin-qt master's >=0.8 glue, which emits a direct call to
logos_module_accept_inbound_token that no backend defines yet -- neither
logos-cpp-sdk's pinned rev nor its master. On ELF that links cleanly with an
undefined symbol; the Windows cross-build is what turns it into an error. Add
those follows in the same wave as the logos-cpp-sdk emitter, not before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ad223c6faf |
Document the two-sided load-time token injection.
Outbound sendToken plus inbound informModuleToken are what lets capability name a caller from the presented token. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
bfbb1998f9 |
chore(deps): bump logos-qt-sdk past the header dedup, and follow its plugin-qt (#185)
logos-qt-sdk#42 handed logos_ui_plugin_context.h to logos-view-module, which
owns it beside the view glue emitter it is a matched pair with. Three comments
here listed it among the headers this repo re-exports; they are corrected.
The bump is not a one-input change, for two reasons found by doing it:
* logos-qt-sdk at this rev requires include/cpp/logos_host_core.h from the
cpp-sdk headers export, and the logos-cpp-sdk pinned here (e3744fb) predates
it -- CMakeLists.txt:122 fails the configure outright. So logos-cpp-sdk goes
to acea0d2 in the same commit (and logos-lidl follows it).
* logos-qt-sdk GAINED a logos-plugin-qt input after c6be61d0 -- the rev whose
ABSENCE of one is what the comment here cited to argue there was "no second
logos-qt-host to collide with". Without a follows the lock resolved qt-sdk's
own logos-plugin-qt (9b2c64e5) alongside this repo's (1aa3e31c): two
resolutions of the repo that owns logos-qt-host, which is a second
TokenManager and every cross-module call refused at runtime with no build
diagnostic. The follows is added beside the url, matching the ones already
there for logos-protocol and logos-cpp-sdk. Lock stays at 211 nodes.
Measured effect on the header set this repo re-exports -- exactly one removal:
removed logos_ui_plugin_context.h
added logos_caller.h, logos_host_core.h, logos_host_services.h,
logos_qt_host_core.h (+ their cpp/ copies)
logos_api.h, logos_api_provider.h, logos_provider_object.h,
logos_qt_arg_decode.h and qt_provider_object.h all SURVIVE despite leaving
qt-sdk's own cpp/: logos-qt-host supplies them, through the `cp -rf` that runs
after the qt-sdk one precisely so its copies win.
Verified: nix build .#checks.aarch64-darwin.tests and .#default both pass.
NOT addressed, and pre-existing: the lock holds SIX logos-plugin-qt nodes, five
at 9b2c64e5 and this repo's root input at 1aa3e31c. That split is byte-identical
before and after this change.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
eeb5cd3270 |
fix(nix): propagate nlohmann_json, and stop pointing at a deleted file
Two small follow-ups to the shared-runtime migration.
PROPAGATE nlohmann_json. This repo re-exports the Qt host runtime headers, and
two of them -- logos_provider_object.h and logos_qt_arg_decode.h -- include
<nlohmann/json.hpp>. Anything compiling against these includes therefore needs
nlohmann on its include path whether or not it has ever heard of nlohmann.
Consumers going through find_package(logos-qt-host) already get it: that package
find_dependency's logos-protocol, which PUBLIC-links nlohmann_json. Consumers
taking the include directory directly do not, and they exist.
Set in TWO places, which is not redundant: symlinkJoin builds a NEW derivation
and does not carry the propagation of the paths it joins. Consumers take the
join, not the headers output, so setting it only on the latter reaches nobody --
measured, a consumer still failed until the join carried it too.
WHAT THIS DOES NOT FIX, measured rather than assumed: a consumer that takes
liblogos as a bare attribute and interpolates ''${logosLiblogos}/include has no
dependency edge for propagation to travel along, so it still needs nlohmann in
its own buildInputs. logos-module-viewer is that shape.
DANGLING POINTER. The comment in src/CMakeLists.txt sent readers to
logos-basecamp/cmake/LogosSharedFromDll.cmake, deleted in logos-basecamp#348 and
logos-logoscore-cli#98. Now says where it went and why.
nix build .#default PASS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b2a9a0ba9d |
feat(shared-runtime): import the runtime instead of providing it (#182)
* feat(shared-runtime): import the runtime instead of providing it
PR-4 of the shared-runtime migration. liblogos_core stops being the single
provider of types it does not own, and becomes a consumer of the libraries that
do: logos-protocol#65 and logos-plugin-qt#22.
WHAT GOES AWAY. The whole if(WIN32) block that absorbed liblogos_protocol.a and
liblogos_qt_host.a with --whole-archive and re-published their symbols through a
generated .def, plus cmake/gen-shared-exports.sh itself. That scheme existed
because PE exports nothing it is not told to export and the definitions lived in
archives that every other image also linked; now they live in shared libraries
that export their own tables, so there is nothing for this repo to re-publish.
WHAT REPLACES IT is one line: logos_sdk carries logos_qt_host_shared rather than
the static archive. logos_qt_host_shared PUBLIC-links logos_protocol_shared, so
the protocol half arrives transitively and correctly layered.
THE INVARIANT IS UNCHANGED. The runtime must still exist exactly once per
process; it is now enforced by there being one shared library per type rather
than by one image absorbing everything. Measured, aarch64-darwin:
liblogos_core.dylib defines 0 (was 32 TokenManager symbols)
imports 8
liblogos_protocol.dylib defines 78 (TokenManager, LogosAPIClient, ...)
liblogos_qt_host.dylib defines 23 (LogosAPI)
OUT-OF-PROCESS CONSUMERS ARE DELIBERATELY UNAFFECTED. Module plugins and ui_qml
backends keep linking the STATIC archive: each runs in its own process where its
own copy is the CORRECT per-process singleton, and a .lgx records an empty nix
closure so it could not carry a shared library anyway.
TWO DEPLOYMENT FAILURES THIS ALSO FIXES, both of which built green.
nix/lib.nix now STAGES liblogos_protocol and liblogos_qt_host beside
liblogos_core, and asserts it did. liblogos_core records
@rpath/liblogos_qt_host.dylib with @loader_path as its only rpath, so the loader
looks for them in that directory and nowhere else. Before this:
nix build .#default OK
logos_host --help exit 0
dlopen liblogos_core.dylib Library not loaded: @rpath/liblogos_qt_host.dylib
A help-text smoke test never touches the library, so nothing in the build or in
a boot check would have caught it. Hence the assertion rather than trust in the
copy loop.
CMakeLists.txt adds both to CMAKE_BUILD_RPATH, mirroring what
LOGOS_PACKAGE_MANAGER_ROOT already does. Without it logos_core_tests aborted at
dyld time, before main(), while .#default had already succeeded.
VERIFIED, aarch64-darwin: .#default OK, dlopen OK, checks.tests PASS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(shared-runtime): install RPATH too, or the installed test binary cannot load
BUILD_RPATH covers binaries run from the build tree; the test derivation runs the
INSTALLED one, which uses INSTALL_RPATH. Only Linux said so -- on macOS the
installed test binary resolved the libraries anyway and checks.tests passed,
while the same commit on Linux died before main() with
error while loading shared libraries: liblogos_qt_host.so
A macOS-green run is not evidence for this class of failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(tests): put the shared runtime on the hand-set Linux RPATH
nix/tests.nix applies its RPATH with `patchelf --set-rpath`, which REPLACES
whatever CMake wrote. So CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH have no
effect on the installed test binaries on Linux, and that list is the entire
search path: anything absent from it is absent at runtime.
Measured: adding both libraries to CMAKE_*_RPATH changed nothing and the suite
still died before main() with
error while loading shared libraries: liblogos_qt_host.so
while the same commit passed on macOS, which does not go through this code path
at all. Two platforms, two independent rpath mechanisms, and only one of them
was wired.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3893c833ec |
chore(deps): relock logos-capability-module onto the cycle cut (#183)
logos-capability-module#25 dropped the builder's logos-standalone-app input,
which broke the dependency cycle
logos-capability-module -> logos-module-builder -> logos-standalone-app
-> logos-liblogos -> logos-capability-module -> ...
flake.lock cannot express a cycle, so Nix was unrolling it 5 times across 23
levels and duplicating the whole subtree at every level. Picking up the fix
collapses this lock:
3,472 -> 211 nodes (2.52 MB -> 0.12 MB, -88,855 lines)
Exactly one root input moves: logos-capability-module c670f7f -> 973f71b.
Every other root input resolves to the byte-identical rev it did before -- the
24 other flakes whose rev SETS shrank were duplicate copies pinned at historical
revs inside the unrolled cycle, never the ones this repo builds against.
Level 1 of 3 (ws update-order): logos-liblogos -> logos-standalone-app ->
logos-module-builder.
|
||
|
|
cb9f27a017 |
chore(deps): relock default-module-loader onto the teardown wait (#181)
logos-module-loader-qt#9 (dece0c8) landed the host-side half of the module teardown contract: between exec() returning and `delete logos_api`, logos_host now asks the module to finish and waits a bounded 3s of the container's 5s grace before proceeding. Everything downstream of liblogos runs modules through that host, so until this relock a module's aboutToUnload() override is simply never called -- the hook exists, compiles, and does nothing. That failure is silent in the worst way: teardown looks normal and the module's cleanup just does not happen. Additive by construction. The host invokes the hook BY NAME through the meta-object rather than the vtable, so a plugin that does not declare it has no such meta-method, invokeMethod returns false, and teardown proceeds exactly as before. Every module built today is in that case. 185/185 tests pass and the library builds against the new pin. Unblocks logos-test-modules#46, the fixture that asserts the contract end to end; it cannot go green until a logoscore built through here carries the wait. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6a16bfffd6 |
chore(deps): relock capability_module onto the universal port (#179)
The url here is unpinned — github:logos-co/logos-capability-module — so it reads as current, while the lock sat at 0cb33fb: the pre-#24 hand-written Qt plugin, from 2026-08-11. capability_module master is c670f7f, the `interface: "universal"` port from its #24: a Qt-free impl over a host-granted trust root, declaring metadata.json#host_services and failing CLOSED until the host grants them. Safe here because this flake's own closure carries the granting side — the loader resolves at acd07cf (logos-module-loader-qt#8, brought in by #178), which stamps the hostServices property and calls logos_module_grant_host_services. Verified in the lock rather than assumed. This matters beyond one input: liblogos is what SHIPS capability_module — nix/bin.nix copies its modules/ into the package and logos_core loads it through initializeCapabilityModule(). So this lock is what consumers actually get, whatever their own flake says. Both logos-basecamp and logos-logoscore-cli were running the old Qt module through exactly this path, behind unpinned urls that looked current. `nix build .#default` and checks.tests both pass. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b949212d6c |
chore(deps): relock default-module-loader onto the host-services grant (#178)
logos-module-loader-qt#8 merged (acd07cf), adding the half of the host-services grant that lives in the loader: module_initializer.cpp stamps the `hostServices` property, fed by qt_plugin_format_loader's hostServicesFor(), which supplies [token_registry, token_delivery] for capability_module. This flake's url for that input is UNPINNED — github:logos-co/logos-module-loader-qt with no rev — so it reads as current in review. Its LOCK, however, had been on e648735c since 2026-08-11, so every consumer built through liblogos kept getting the pre-grant loader hours after #8 landed. The symptom is silent. capability_module comes up ungranted, logos::host:: tokenKeys() returns ungranted, requestModule refuses to mint, and the caller's outbound call returns a DEFAULT with no error surfaced: call orchestrator_module greetThrough World -> {"result":"","status":"ok"} which is how it presents in logos-capability-module's doctests (25 passed, 3 failed) while that repo's own build-and-test passes on both platforms — the unit tests drive the impl directly, the doctest drives the whole runtime. Only the lock changes; flake.nix is untouched. `nix build .#default` passes. Consumers pinning liblogos need their own relock to see this — logos-logoscore-cli in particular, whose master lock is what logos-capability-module's doctests build logoscore from. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
93207e4141 |
chore(deps): track protocol and plugin-qt master (#177)
* fix(windows): point the shared-runtime .def at logos-qt-host, and fail loudly
The Windows single-provider scheme makes liblogos_core.dll the one provider of
the shared C++ runtime, and it names its inputs by CMake TARGET. logos-qt-sdk no
longer carries an archive — the Qt host runtime moved to logos-qt-host — so
$<TARGET_FILE:logos-qt-sdk::logos_qt_sdk> no longer resolves and the whole
mechanism had to be repointed.
More importantly, the old shape failed OPEN. The guard was
if(WIN32 AND TARGET logos-protocol::... AND TARGET logos-qt-sdk::logos_qt_sdk)
so a missing target did not error — the condition simply went false and the
--whole-archive link, the nm scan and the generated .def were all skipped
SILENTLY. The result is the split-brain this file exists to prevent: main_ui and
ui-host each end up with their own TokenManager and every cross-module call is
refused. The code already fails loudly when `nm` is missing, for exactly this
reason; it had no equivalent guard for the target being absent.
Now the target tests are a foreach + FATAL_ERROR inside if(WIN32), so a missing
provider stops the configure instead of quietly restoring the split-brain.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(access-policy): say ON/OFF out loud, and pin the flag both directions
Deny-by-default enforcement already existed here: `mode: "enforce"` is the
switch, and under it computeDerivedAllowedCallersLocked derives each target's
allowed callers from the declared dependency graph. Nothing about that
changes — this makes the switch legible and pins its contract.
setAccessPolicy now states which side it landed on for every input (no
policy / unparseable / non-enforce mode / enforce). Enforcement that silently
failed to arm is the dangerous outcome: it looks identical to enforcement
that is working and simply has nothing to deny, so an operator who mistyped
`"mode":"enforced"` previously got a wide-open runtime and a clean log.
DenyByDefaultFlagTest drives one scenario through the flip: `declared`
declares `target`, `undeclared` declares nothing. Flag off, the target has no
restriction at all (today's behaviour). Flag on, `declared` is on the list and
`undeclared` is not. The declared half carries the weight — an implementation
that refused everything would pass the denial half on its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(b4): link the Qt host runtime from logos-qt-host, not logos-qt-sdk
B1 moved LogosAPI / LogosAPIProvider / LogosProviderBase / PluginInterface
out of logos-qt-sdk into logos-plugin-qt, published as the CMake target
logos-qt-host::logos_qt_host. This repoints liblogos at that target so B2b
can delete logos-qt-sdk's forwarders.
The host runtime is found DIRECTLY: add logos-plugin-qt as an input (pinned
to the B1 rev, since logos-qt-host is not on its master yet) with
logos-protocol / logos-nix / nixpkgs following, and find_package it against
a new LOGOS_QT_HOST_ROOT. It is deliberately not inherited through
find_package(logos-qt-sdk) -- qt-sdk does not carry a dependency on the host
runtime today, and will carry even less of one after B2b.
That also repairs the Windows single-provider block. It already named
logos-qt-host::logos_qt_host, but its comment claimed the target arrived via
find_package(logos-qt-sdk), which was false -- so its FATAL_ERROR guard would
have fired on the first real Windows build. The guard is unchanged (still a
hard error, never a silent skip); its premise is now true.
logos-qt-sdk stays an input, for the developer headers nix/include.nix
re-exports (logos_ui_plugin_context.h and friends) -- not for the host
runtime. Its now-unused -DLOGOS_QT_SDK_ROOT flag is dropped so CMake does not
warn about an unused variable; the env entry stays.
Two silent skips converted to hard errors along the way:
- tests/CMakeLists.txt guarded its SDK include dirs with `if(EXISTS ...)`,
so a bad root compiled the tests against a different copy of LogosAPI
than they link. Now FATAL.
- nix/include.nix copies the host headers over the qt-sdk ones so consumers
of this prefix see the declaration liblogos_core actually links (qt-host's
carries LOGOS_SHARED_API, the dllimport that keeps Windows on one
TokenManager). Everything copied before it is mode 0444 out of the store,
so a plain `cp -r` fails with EACCES and the existing `|| true` would have
swallowed it -- hence chmod + `cp -rf`, plus an assertion that the
installed logos_api.h really is qt-host's.
Verified on aarch64-darwin with local overrides for cpp-sdk, qt-sdk, protocol
and plugin-qt: logos-liblogos-lib, -include, -tests, default and portable all
build; the `tests` check runs 185 tests, 0 failures. Pointing
LOGOS_QT_HOST_ROOT at a nonexistent path fails the build with the intended
FATAL_ERROR rather than falling back. The header prefix is a strict superset
of the previous one (58 -> 63 files, none removed).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(deps): raise logos-protocol to the rev logos-qt-host actually needs
b3b2e50 repointed this repo from logos-qt-sdk's archive to logos-plugin-qt's
logos-qt-host, but left logos-protocol on master (03842db). That made the
repoint INERT: the tree did not build at all.
cpp/logos_api.cpp:38:52: error: no member named 'forIdentity' in 'TokenManager'
cpp/logos_api.cpp:48:24: error: no member named 'isolateIdentity' in 'TokenManager'
cpp/logos_api.cpp:57:50: error: no member named 'forIdentity' in 'TokenManager'
logos-qt-host's LogosAPI is built on the per-identity token store, and protocol
master has none of it -- 03842db's TokenManager carries only instance(),
m_tokens and m_mutex. The identity API arrives in c8bab12
(feat/per-client-token-store), so that is the floor for consuming qt-host at
all. The rev is pinned in the url, not just the lock, for the same reason
logos-plugin-qt already is: it is not on master, so a bare url would let
`nix flake update` silently walk this back to three compile errors.
The rev is also not merely "new enough". It is the SAME rev logos-basecamp and
logos-standalone-app pin, and that identity is the point. liblogos_core, the
app image and every in-process UI plugin share one TokenManager; two protocol
generations across that boundary give two token stores, which is the
"ModuleProxy: rejecting unauthorized call ... auth token not recognized"
failure the Windows .def block in src/CMakeLists.txt exists to prevent. On PE
that shows up as duplicate definitions; on Mach-O the second store is simply
linked into whichever image referenced a symbol liblogos_core failed to export.
Which is exactly what was happening here: with no forIdentity to import, a
consumer drags logos_api.cpp.o out of the static archive and token_manager.cpp.o
comes with it.
Only logos-protocol moves. logos-cpp-sdk, logos-qt-sdk, logos-plugin-qt and
default-module-loader already `follows` it, so all four now compile against
c8bab12 and the lock diff is one node.
Verified on aarch64-darwin, every package and check built by name, all EXIT=0:
default, logos-liblogos, -bin, -include, -lib, -modules, -tests, portable, and
the `tests` check -- 185 tests, 0 failures. The acceptance measurement on the
built library:
nm -gU lib/liblogos_core.dylib | grep -c LogosAPI11forIdentity -> 1 (was: no build)
nm -gU lib/liblogos_core.dylib | grep -c TokenManager8instanceEv -> 1 (still the provider)
liblogos_core now exports the whole identity surface -- LogosAPI::forIdentity,
TokenManager::forIdentity / isolateIdentity / isIsolated / seedBootstrapTokens
-- so consumers import them instead of re-linking a second copy.
NOT fixed here, and blocking on other repos:
- packages.x86_64-windows does not evaluate on this branch:
`attribute 'x86_64-windows' missing` at logos-plugin-qt.packages.<system>.
logos-qt-host. logos-qt-sdk exposes a windows pseudo-system via
forAllTargets/mkWindowsPkgs; logos-plugin-qt has only forAllSystems over the
four real systems. So b3b2e50 traded a Windows-capable provider for one that
is not, and the Windows single-provider machinery this repo owns cannot be
evaluated, let alone measured, until logos-plugin-qt grows that target.
liblogos master (
|
||
|
|
503587797d |
feat(windows): cross-compile liblogos, and make liblogos_core the single provider (#176)
* feat(windows): cross-compile logos-liblogos for x86_64-w64-mingw32
Adds the x86_64-windows pseudo-system to `packages` (checks and devShells stay
native). liblogos's own src/ needed NO portability work at all -- verified
exhaustively, not assumed: 17 files, 2304 lines, zero POSIX headers and zero
POSIX APIs. All process, plugin and socket work already lives in
logos-container-subprocess and logos-module-loader-qt, which were ported first.
Two real blockers, one of them silent:
* install(TARGETS logos_core ...) named only LIBRARY and ARCHIVE destinations.
A DLL is a RUNTIME artifact, so CMake SKIPPED IT WITHOUT COMMENT: the build
succeeded and shipped a lib/ containing liblogos_core.dll.a and no DLL at
all -- a link-only package that would have handed logosctl.exe an import
library with nothing behind it. Proven by reverting the fix: rc=0, no DLL.
lib.nix now refuses to produce an output with no loadable logos_core.
* The test suite is genuinely POSIX-only (spawn.h, sys/wait.h, mkdtemp, kill),
and CMake put it in the default `all` target, so it broke the cross build
before anything else could. Tests are now gated behind LOGOS_BUILD_TESTS,
with the gate wrapping the gtest FetchContent fallback too -- otherwise
dropping gtest sends CMake to the network inside the sandbox and it dies on
a misleading "downloading ... failed".
Also: package_manager_lib needed IMPORTED_IMPLIB (mingw links against the
import library, not the DLL), and Qt's host tools come in via
logosQtCrossCmakeFlags.
Verified: liblogos_core.dll is a PE32+ DLL whose export table carries the C
ABI (logos_core_init / _start / _load_module / _get_loaded_modules). The
output also ships logos_host_qt.exe with its 15 runtime DLLs and
capability_module_plugin.dll. Native unchanged: 181 tests, 174 passed, 7
skipped -- the same 7 ProcessManagerTest cases skipped before this change.
* fix(windows): carry the package-manager DLL closure, and type the manifest
lib.nix (Windows only): copy every *.dll / *.dll.a from the package-manager
root rather than just libpackage_manager_lib*, with a loud guard if liblgx.dll
is missing -- an absent runtime DLL otherwise shows up as an executable that
exits with no output at all.
modules.nix: emit "type": "core" in the generated manifest. Native-visible
but strictly additive.
* fix(windows): export only the C API from liblogos_core
liblogos_core.dll exported 13,252 symbols. Eighteen of them were the
logos_core_* C API; the rest were the entire internal C++ surface,
LogosAPI's included. Any consumer linking both liblogos_core and the
qt-sdk static library therefore failed with
multiple definition of `LogosAPI::LogosAPI(QString const&, QObject*)'
...liblogos_qt_sdk.a(logos_api.cpp.obj)
first defined here: ...liblogos_core.dll.a(...)
which is what blocked main_ui from linking.
Root cause: LOGOS_CORE_EXPORT expanded to
__attribute__((visibility("default"))), an ELF concept that mingw-gcc
accepts and silently ignores on PE. Nothing was explicitly exported, so
an earlier fix reached for -Wl,--export-all-symbols to get the C API out
-- correct as far as it went, and the reason the whole C++ surface
leaked with it.
Using __declspec(dllexport)/dllimport on Windows fixes it at the root and
does so twice over: GNU ld disables PE auto-export image-wide as soon as
any symbol is dllexported, so the internal surface stops leaking as a
side effect. Measured: logos_core_* exports unchanged at 18, mangled C++
exports 13,673 -> 452, LogosAPI no longer exported at all.
Two copies of LogosAPI (host and plugin) is the DESIGNED arrangement, not
a regression -- see logos_qt_lp_bridge.h, which notes a Qt plugin links
its own copy of the protocol library so TokenManager::instance() inside
it is deliberately not the host's, and syncTokens is the bridge. That
holds on Unix too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(windows): export the shared runtime from liblogos_core.dll
Makes liblogos_core the single provider of TokenManager, LogosAPI,
LogosAPIClient and the LogosResult stream operators, so the Basecamp
process has ONE of each instead of nine.
The export set comes from a GENERATED .def rather than dllexport in the
headers, because the definitions live in liblogos_protocol.a /
liblogos_qt_sdk.a -- archives also linked by logos_host.exe, ui-host.exe,
every module plugin and every native platform. Annotating them for export
would mean a second, Windows-only, export-annotated build of both
archives kept in sync forever, to solve a Windows-only problem. Exporting
at this link instead leaves those archives compiled byte-identically.
Two details in gen-shared-exports.sh that look like they could be
simplified and cannot:
* It exports the WHOLE archive, not a curated class list. ld picks
archive members by object file for reasons unrelated to our symbols
-- measured here, main_ui referenced std::string's move constructor
and ld satisfied it from logos_api.cpp.obj, dragging LogosAPI,
LogosAPIClient and TokenManager in behind it. Consumers link an empty
archive and take everything from the DLL, which only works if the DLL
really provides everything; a partial export set surfaces as an
undefined reference in a downstream repo, far from the cause.
* It filters COMDAT symbols out. Those come from inline functions and
templates in headers, so every consumer TU emits its own copy
regardless; exporting them makes the import library a strong
definition that collides with that copy.
Measured after: liblogos_core.dll goes from 18 exports to 376, of which
132 are the shared runtime; LogosBasecamp.exe drops 14.3 MB -> 1.08 MB
and main_ui.dll 23.2 MB -> 10.1 MB as the duplicated statics stop being
linked in. Native aarch64-darwin is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Merge origin/master into feat/windows-cross, and re-pin the L1-L7 inputs
master landed
|
||
|
|
56aa8bb45c | chore: bump logos package manager | ||
|
|
2f4162a97f |
chore: pin logos-protocol 0f26ffd, and move CI off Nix 2.22 (#174)
liblogos pinned logos-protocol at 3a31c91 (Jul 31). Bring the root pin to
master, 0f26ffd (Aug 6) — the span is exactly two commits, protocol #40
(lp_invoke_async can report a failure) and #41 (report failures that happen
after acquire, without moving the ABI).
Why per-repo pins rather than `follows` overrides: logos-protocol is
statically linked (`nix path-info -r` on the tests output shows zero
protocol store paths), and liblogos_core.dylib alone carries 299 weak
external definitions. On macOS, when two images built against different
protocol revisions land in one process, dyld coalesces those weak
definitions and one image's protocol code silently binds to the other's —
across a real ABI change, since #41 moved
PlainLogosObject::awaitCompletion from (QString const&, int) to
(QString const&, int, QString const&, logos::CallError*). Every repo in
the chain pinning the same protocol is what makes that a non-issue; no
override is needed anywhere.
The CI installer bump is hygiene bundled in, not a requirement for the
pin: cachix/install-nix-action@v27 installs Nix 2.22.1, which mis-handles
several flake locking features. @v31 installs 2.35.1, matching what
logos-standalone-app's CI already uses (and its inline comment already
explains why v27 was not good enough there either).
Verified on aarch64-darwin: `.#logos-liblogos-tests` builds and
logos_core_tests reports 181 run / 176 passed / 5 skipped — byte-identical
to the pre-bump baseline at
|
||
|
|
f63cda3891 |
chore: bump the module loader for the host logging fix (#173)
logos-module-loader-qt#5 makes logos_host install its own Qt message handler, so a module's diagnostics reach the daemon that spawned it instead of being diverted to journald by Qt's default backend. liblogos is the carrier: nothing downstream sees that fix until this pin moves. Measured end to end on Linux with the two halves in place (this loader plus logos-logoscore-cli#83, which fixes a -v that never reached the daemon and raises spdlog's level so the forwarded records survive): module lines in a session log went from 1 to 46, and the 59 lines per install that were escaping to journald went to 0. Single-input bump; nothing else in the lock moved. `nix flake check` green. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ee5c553497 |
chore: bump logos-protocol to the off-strand socket close fixes (#172)
4ee85b26 -> 3a31c91d. liblogos_core statically links logos_protocol, so the plain-transport code in this library is a copy of whatever this pin says — which is why the pin matters here and not just downstream. The two fixes this carries are the same race on two different asio objects: #38 RpcConnection::fail() closed the socket on the caller's thread while every other access to m_stream was serialized on m_strand. Teardown (~RpcClient -> ~PlainTransportConnection -> stop() -> fail()) ran close() -> cleanup_descriptor_data(), nulling impl.reactor_data_, while the io worker was inside reactive_socket_service_base::start_op() for a just-posted write. SIGSEGV at +0x98 on the IoContextPool thread. #39 The acceptor half: RpcServerTcp/Ssl::stop() closed m_acceptor on the host thread (via ~PlainTransportHost) while doAccept() re-armed async_accept from its own completion handler on the io worker. Both now hand the close to a strand with dispatch(). Measured upstream at ~0.45% of calls over tcp and tcp_ssl, 0 over local/QtRO. It is a false negative, not a lost result: the RPC completes and prints the right answer, then teardown crashes, so anything that checks the exit code before parsing stdout reports a healthy call as failed. Also swept up between the two pins: #35 jsonToLogosResult and #37 Codec<std::optional<T>>. Both additive. Verification on aarch64-darwin: - nix build .#checks.<sys>.tests: 181 tests, 16 suites, 0 failures — identical to origin/master (181/16/0), so no regression and no new coverage; protocol's own regression tests live in logos-protocol. - the fix is present in the artifact, not just the lock: the new private methods appear in the built liblogos_core.dylib — closeStreamOnStrand x16 (#38), RpcServerTcp/Ssl::closeAcceptorOnStrand (#39) — and are absent from the same symbol table built at origin/master. Every logos-protocol edge on the strand that feeds liblogos_core now agrees at 3a31c91d: root, logos-cpp-sdk, logos-qt-sdk and default-module-loader (logos-module-loader-qt). One sibling still carries its own older protocol — see the PR description for logos-capability-module. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4ef9736a1e |
chore: bump logos-module to the object-form dependency fix (#171)
logos-module 9fb81c5 (#22): a dependency entry written in the manifest's object form ({name, version?, signer?}) was read with toString(), which yields an empty string for anything but a JSON string, so the entry was dropped and the module it names never became a dependency. ModuleRegistry reads a module's declared dependencies through this library, so this pin is what decides whether such an entry resolves. |
||
|
|
330f1cc496 |
chore: bump logos-protocol to the whole-valued-float fix (#170)
logos-protocol -> 4ee85b2 (#32 whole-valued floats, #33 path threading, #34 adopted byte coverage) logos-cpp-sdk -> 5f63af6 liblogos was pinning c0df466, which is #31's signedness check WITHOUT #32's follow-up — the version that rejected 3.0 as well as 3.7. That matters more here than in a leaf consumer: the daemon embeds liblogos, so its protocol pin is what a `logoscore call` actually runs, no matter what logoscore-cli pins directly. liblogos does not declare a follows for protocol, so bumping the consumer alone leaves this copy behind. logos-logoscore-cli master has been red on doc-tests since it picked up c0df466 (`call test_basic_module addInts 40 2` fails); this is the upstream half of that fix. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6f0ca4b9b0 |
chore: bump logos-protocol to the signedness + sentinel fixes (#169)
logos-protocol c0df466 (#31): * Codec<T> checks integer signedness and range, so a negative can no longer wrap into an unsigned and a wide value can no longer truncate. * the pending-call sentinel is matched by shape rather than key presence. All four detection sites used a bare contains(), so any user map carrying that key was taken for a deferred call and hung for the full timeout. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7202ea19f1 |
chore: bump logos-protocol to the uint64 fixes (#168)
logos-protocol 8b8a358 (#30) — two places where a uint64 above int64max stopped being itself: * the universal -> Qt EVENT bridge converted with QJsonDocument::fromJson + QJsonValue::toVariant instead of the canonical helper the method path uses, so uintEvent(2^64-1) arrived as 1.8446744073709552e+19 while the equivalent method return was exact. The same bridge also failed to decode canonical tagged bytes into a QByteArray. * the plain (tcp/tcp_ssl) wire had no unsigned alternative in RpcValue, so the same value wrapped to -1 — silently, and independently in each direction. Retires M6 from the LIDL conformance matrix. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5c8b9f06f4 |
chore: actually pin logos-protocol to 362b03f (#167)
#166 merged a lock whose node numbering differed from master's, so the textual merge left the ROOT logos-protocol input on 6401e30 while 362b03f landed in a transitive slot. Re-running the update on master still moves the pin, which is the proof it never landed. liblogos is the module host and consumers deliberately do not follows its protocol, so this pin drives the host path for everything that links it. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3e548c7582 |
chore: bump logos-protocol and logos-cpp-sdk (#166)
logos-protocol ae2f7e1 -> 362b03f one canonical LIDL <-> JSON codec (#29) logos-cpp-sdk 350a289 -> 3d322bd 64-bit int/uint, records, cdylib composites (#111, #113) The protocol bump is the substantive one: it folds the Qt and plain-wire copies into the shared codec, which fixes a bstr nested in a container being UTF-8 mangled, an empty nested bstr arriving as null, and a uint64 above int64max degrading to a double once nested. 192 tests pass. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
be221c5749 |
chore: bump protocol + qt-sdk + capability-module + module-loader-qt (multi-user + token validator) (#165)
Pulls the whole multi-user runtime together with a consistent protocol: - logos-protocol -> 6401e30 (#20: group-shareable sockets, reaper, bind-failure + ModuleProxy transport-aware token validator) - logos-qt-sdk -> 2ec5945 (#11: LogosAPIProvider::setTokenValidator) - logos-capability-module -> 390486e (#21: rebuilt against the new protocol, so core_service and capability_module no longer skew) - default-module-loader (logos-module-loader-qt) -> bc46dbf (#4: logos_host quits cleanly on SIGTERM/SIGINT so module local sockets are unlinked, not leaked) logos-liblogos + logos-liblogos-lib build green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
64de644ec6 |
chore: re-pin logos-protocol to master (LogosAPIConsumer handle cache) (#163)
Picks up logos-co/logos-protocol#24 (664b43f): LogosAPIConsumer caches the remote-object handle per name across sync + async calls (no per-call QtRO replica acquire). qt-sdk/cpp-sdk follow logos-protocol, so this one bump threads the fix through logos_host/ui-host. Consumer-side, ABI-compatible. |
||
|
|
8ebb808dfb | chore: bump logos-protocol to master (nested-bytes qvariantToNlohmann fix #23) (#162) | ||
|
|
b8de686bce |
fix(tests): avoid core boot during gtest discovery timeout (#160)
* fix(tests): avoid core boot during gtest discovery timeout gtest_discover_tests defaulted to POST_BUILD discovery mode, which runs the freshly-built logos_core_tests binary with --gtest_list_tests at build time. test_app_lifecycle.cpp's main() called logos_core_init() before gtest parsed its flags, so even a bare test-listing booted the full Qt core (QCoreApplication + subprocess/socket/IOKit) and loaded the entire dylib closure. In the Nix sandbox this blew past the 5s TEST_DISCOVERY_TIMEOUT, killing the binary with empty output and failing the whole derivation (cascading up to run-logos-standalone-ui). Fix, two parts: - CMakeLists.txt: use DISCOVERY_MODE PRE_TEST so enumeration is deferred to ctest time; the binary never runs during the build. This alone deterministically fixes the failure. - test_app_lifecycle.cpp: run InitGoogleTest first and return early when only listing tests, before logos_core_init(). Safety net so listing never boots the core regardless of discovery mode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(tests): use GTEST_FLAG_GET for list-tests guard Read the list_tests flag via GTEST_FLAG_GET(list_tests) instead of ::testing::GTEST_FLAG(list_tests). GTEST_FLAG(name) is only a plain bool in the non-Abseil gtest build; with the Abseil flags backend it is an absl::Flag object, so the raw macro is not portable in a boolean context. GTEST_FLAG_GET dispatches correctly for both backends. The macro already qualifies with ::testing:: internally, so it must be used unqualified — prefixing it (::testing::GTEST_FLAG_GET) expands to a doubled ::testing::::testing:: token and fails to compile. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
819faac420 |
feat: add logos_core_get_modules_info() for generic module introspection (#159)
* feat: add logos_core_get_modules_info() for generic module introspection New C API returning a JSON array describing every known module — one object each with name, path, loaded flag, direct dependencies, direct dependents, and the full embedded metadata (parsed from the plugin's declarative metadata.json; null when unreadable). ModuleInfo now caches the raw metadata JSON at discovery (via ModuleLib::LogosModule::getRawMetadataJson, no plugin instantiation), ModuleRegistry::allModulesInfo() assembles the array under the registry lock, and ModuleManager exposes it as getModulesInfoJson()/CStr. Methods/events are intentionally excluded: they require instantiating the plugin in-process, which would defeat the subprocess-isolation model for a bulk "all known modules" query. Tests: ModuleManagerTest.GetModulesInfo_* (shape, empty) and RealModuleRegistryTest.GetModulesInfo_PopulatesEmbeddedMetadata (real plugin metadata via TEST_PLUGIN). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump logos-module to merged getRawMetadataJson (#21) Re-pin logos-module a3e288a → 2ec64c4 (master, includes #21) so the modules-info API builds against the merged getRawMetadataJson without an override. Full test suite green on this lock (181/181). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: record module load timestamp (loaded_at) in modules-info Add ModuleInfo::loadedAt — a unix-seconds timestamp stamped by markLoaded (both overloads) and cleared to 0 by markUnloaded, so loaded_at is 0 ⟺ not loaded (reload re-stamps it). Surfaced as "loaded_at" in logos_core_get_modules_info(), letting callers derive a module's uptime as now - loaded_at (valid only while loaded). Tests: GetModulesInfo_ReturnsRichEntryPerModule now asserts loaded_at is 0 for an unloaded module and > 0 for a loaded one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
87ae7ce5db | chore: bump logos-protocol (#158) 0.2.0 | ||
|
|
a86ca28971 |
chore: bump logos-protocol to concurrent-dispatch + pin host transport (#157)
The bundled QtRO host (logos_host_qt, from default-module-loader =
logos-module-loader-qt) was built against the pre-concurrent-dispatch protocol
(9de4165a) while liblogos_core moved with logos-protocol — and default-module-loader
was a bare input with no follows, so it stayed stale. A c6234940 plugin's
capability-token handshake was therefore rejected ('auth token not recognized')
inside the host (wallet-ui/basecamp UI never reached Ready).
Add default-module-loader.inputs.{logos-protocol,logos-cpp-sdk,logos-qt-sdk}.follows
so the host transport is always built against the same protocol as liblogos_core,
and bump logos-protocol to current master (c6234940). No C++ source changes — every
host call site uses signature-stable API.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
bcccf2db65 | chore: update logos-package in flake.lock (#156) | ||
|
|
0c0a23db48 |
deps: bump logos-module-loader-qt to the process-group isolation fix (#155)
The host was extracted from liblogos into logos-module-loader-qt (#149/#151/#152), which left liblogos pinned at module-loader-qt 08213eb — a host WITHOUT the subprocess process-group isolation + parent-death cleanup (the setsid() + PR_SET_PDEATHSIG + getppid-watchdog that lived on the pre-refactor |
||
|
|
dbcafea05c |
extract subprocess container and container abstraction, module loader abstraction and qt module loader p2 (#152)
* update flake * make default container and default module loader something configurable on build * update flake * ci fix * update flake |
||
|
|
f36485ba86 |
extract subprocess container and container abstraction, module loader abstraction and qt module loader (WIP) (#151)
* extract subprocess container and container abstraction * decouple receiving token on a module loader from the container * move shim subprocess_manager to tests/ * add notes about potentially moving container registration and module_loader registration to the frontends * extract module loader abstraction and qt module loader * remove module_name_validation.h; overkill for just one function |
||
|
|
f463512ed3 | add draft doctest showing how to use liblogos as a library (#150) | ||
|
|
5f81397237 | rename 'runtime' to 'module_loader' (#149) | ||
|
|
63d95e20dd | improve logs; use spdlog (#148) | ||
|
|
050f2d3628 |
Qt-split retarget + logos_protocol_version load gate (#142)
* Qt-split retarget + protocol-version load gate - Link the split SDK stack: logos-qt-sdk (LogosAPI/provider glue; the logos_sdk alias now points at logos-qt-sdk::logos_qt_sdk, chaining logos-protocol) + Qt-free logos-cpp-sdk headers. - Protocol-version load gate (the first real consumer of module metadata pre-load): ModuleManager reads the module's embedded logos_protocol_version before runtime.load() and applies the one compatibility rule — equal protocol MAJOR loads, different MAJOR is refused with a diagnostic naming both versions, missing/unparseable stamp (pre-protocol modules) loads permissively with a warning. The decision logic is std-only (logos_core/protocol_gate.h) and unit tested (refuse bumped major / warn-load legacy / silent minor skew). - ModuleDescriptor.rawMetadata is now actually populated for runtimes. * lock: pin extraction-chain branch revs for standalone CI Temporary — drop when the chain PRs merge (re-lock against masters). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: pin logoscore-cli to its qt-split branch head The doc-test builds logoscore-cli at latest master with only liblogos overridden to the commit under test; master logoscore-cli cannot build against qt-split liblogos. Pin the runtime to the chain branch (logos-co/logos-logoscore-cli#43) so the doc-test exercises the coherent stack. Temporary — revert to the unpinned URL when the chain merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * host: surface the spawn auth token as a LogosAPI property cdylib-authored modules run their own statically-linked protocol stack whose TokenManager is a separate copy of the singleton; the generated Qt glue reads this property (cross-image-safe, like modulePath) and seeds the cdylib's stack via logos_module_accept_token so the module's outbound calls authenticate. * host: set the authToken property before registerObject registerObject runs the provider object's init() — where the cdylib glue reads the property. Setting it afterwards meant cdylib modules always saw an empty token. * lock: protocol+cpp-sdk merged to master — pins advance (protocol 9de4165, cpp-sdk f0fe8cb, qt-sdk 722e590) * lock: qt-sdk#1 merged — pin advances to qt-sdk master * gate: drop QJson from the Qt-free core — parse rawMetadataJson with nlohmann The protocol-version load gate had pulled QJsonDocument/QJsonObject into src/logos_core (Qt-free territory). logos-module now exposes the embedded metadata as a compact JSON string, so the gate reads it via nlohmann and the std::string extractMetadata overload. * lock: logos-module b42805d (result-lm untracked) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
66256b54ef | update logos-capability-module and logos-cpp-sdk (#147) | ||
|
|
2bb5b89f3c | enforce access policy based on dependencies (#146) | ||
|
|
76416d32fc | fix: f_014: prevent unbounded accumulation of child module stdou (#144) | ||
|
|
098fed4527 | support setting access policy (#145) | ||
|
|
68e408a5de | add api for access policy (#143) | ||
|
|
b396b36b5e |
test: assert auto-resolution leaves transitive dep closure loaded (#141)
LoadWithDeps_LoadsInTopologicalOrder pins the load *call sequence* when
loading a module with_dependencies=true. Add a complementary test that
pins the *observable end state* via the public logos_core_is_module_loaded
query: requesting a single top-level module must leave its entire
transitive dependency closure loaded.
Uses a diamond (app -> ui, core; ui -> core) so it also proves a
dependency reachable by two paths is loaded exactly once, not skipped or
double-loaded. This is the guarantee callers actually depend on ("load
app, get everything it needs").
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
eb0d30095d | fix: f-030: sanitize malicious names used in sockets (#138) | ||
|
|
fedb496720 | fix: F-022: add blocklist for certain module names (#139) |