* feat(consumer): logos::admitConsumer — one home for admitting a non-module
Two hosts hand-rolled the same three steps independently — isolate an
identity, mint a credential, register it with capability_module — and the
duplication had already produced a bug: basecamp did the registration
inside the has-a-backend branch, below an early return, so pure-QML plugins
registered nothing. It survived only because those calls went out on the
host's ambient ring, where every token already existed and the handshake
was never reached. Remove the ambient ring (logos-protocol #71) and that
becomes a hard failure.
So the operation gets one home. admitConsumer isolates, mints, registers
and installs the credential as a single step, and hands back a
ConsumerIdentity. Both hosts lose their private copies: basecamp -16 net
lines, standalone-app -14.
It lives here rather than in logos-liblogos, which is where it was first
proposed. Both hand-rolled sites call LogosAPI, which is this repo's, and
the operation needs a capability_module client to register through — going
via liblogos's C API would add a hop for hosts already holding the Qt
object. logos-liblogos needs no change at all.
Named for what it does: a CONSUMER is admitted, not registered as a module.
It is never published to the registry, never callable, never in
--modules-dir.
Two guards, because the sharp edges here are silent ones:
* adoptConsumerCredential REFUSES an isolated store. It exists for a
co-process adopting its parent's credential into its own process ring,
where the store IS that ring. Pointed at an in-process private store it
would install whatever it was handed — including the host anchor —
straight past adoptCredentialFor's refusal, putting the elevation #71
removes one call away again.
* A #error when logos-protocol is newer than the contract this file
implements. The wave order is mandatory and was unenforced: bump the
protocol alone and every isolated identity gets an empty store, while
nothing fails to build (every MINOR guard in the fleet is >=) and the
integration tests stay green (the ui-host half keeps working off its
stdin credential). The bound fired on its first build — this change
ships protocol 0.7 and it was set to 6 — and was proven non-inert by
going red at 0.8.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(deps): relock logos-protocol onto master, which now carries #71
admitConsumer calls TokenManager::adoptCredentialFor and adoptCredential.
Both arrived with logos-protocol#71 ("a private store is created EMPTY"), so
the lock had to move past it — this branch still pinned 6c24fcb1, which is
0.6.0 and has neither:
logos_consumer.cpp:89: error: 'adoptCredentialFor' is not a member of 'TokenManager'
logos_consumer.cpp:155: error: 'class TokenManager' has no member named 'adoptCredential'
Now b37a2e9f, protocol master with #71 merged.
This is the wave order the header documents, seen from the other side: the
empty-store change lands in protocol, and everything that makes it survivable
lands here. The guard in logos_consumer.h fires when this repo is BEHIND the
protocol; this commit is the ordinary case of catching up to it.
Verified on x86_64-linux with the relocked input:
consumer-admission PASS
qt-host PASS
caller-contract PASS
glue-compiles PASS
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>