Skip to content

Releases: mchen6/countinghouse

v6.1.0: countinghouse 6.1.0

Choose a tag to compare

@mchen6 mchen6 released this 26 Aug 11:39

countinghouse 6.1.0

Two new subsystems, both additive. api.json, schema.json and the MCP
contract are unchanged -- tools/list is byte-identical to the golden
sample, asserted on every commit. No module, auth config or deployment
that works on 6.0.0 needs changing.

Composition: modules call each other by name
A handler reaches another module as /. --
repo-scan/scanService.scan -- through ctx.call(address, input, opts),
instead of a pasted deviceID UUID, a service URN and a hand-built
client. What a module may call is declared in its own package.json
("countinghouse.calls"); which identity its hops are authorized as is
bound separately by the operator, in the auth config ("runsModules"),
because that is the operator's decision and not the author's. Both auth
backends implement it.

Everything is verified at load time, not at first call: every declared
address is resolved against the target's real spec, the identity is
bound, a duplicate binding is refused, and the bound identity's grant to
each target is checked. A typo fails the module at startup, naming the
module, the address and the file to fix.

Authorization and billing stay two different identities -- a hop is
authorized as the bound identity and billed to ctx.caller, the real
outer caller -- so per-hop cost lands on whoever made the outer call.
ctx.serviceClient remains the escape hatch for a per-call identity
override or a module that needs two identities.

The module-authoring toolchain
--authoringTools (default off) exposes countinghouse_validate_plan,
countinghouse_validate_module, countinghouse_load_module and
countinghouse_call_tool behind a second, independent admin-key gate.
Default-off is not precautionary: load_module plus call_tool is
arbitrary code execution with a friendly name. With the flag off the
tools are byte-identical to tools that do not exist, from the same line
of code that answers a genuinely unknown tool.

countinghouse-validate is a new bin: the same oracle from a shell, no
server required, --json for machine-readable output. validate_module
runs the module under test in a spawned child process, so caller-
supplied code that crashes or calls process.exit() kills the child
rather than the gateway.

Fixes
ctx.call's refusals carry typed error codes -- CTX_CALL_NOT_READY,
CTX_CALL_UNBOUND, CTX_CALL_UNDECLARED, CTX_CALL_BAD_ADDRESS,
CTX_CALL_UNRESOLVED -- reaching an MCP client in
structuredContent.code. They used to collapse into the generic
DEVICE_INVOKE_EXCEPTION, which made a misconfigured chain
indistinguishable from a callee that genuinely crashed. The startup
window, where verification has not finished but the device is already
serving, now answers CTX_CALL_NOT_READY ("retry") instead of blaming an
auth config that was already correct.

A bare instance completes discovery. allmodulediscovered was gated on a
startup module count that an instance with no --loadModule flags never
reached, so verification never ran there -- exactly the --authoringTools
workflow, which is designed to start with no modules.

A deviceID already registered by a different module is refused rather
than silently taking over. Re-verification after a runtime load no
longer purges devices that are already online. errCode rides the
main-thread-routed reply envelope, so an inner hop's code survives the
worker boundary on the default path as it already did under
--directPeerChannels. The heap-stat timer is installed once per process,
not once per discovery event.

spec/schema.json relaxes a device's modelDescription to 4096
characters, matching what an action's description already allowed.

Docs
examples/repo-review, a composite tool that reads a repository and never
returns the source it read, with npm run demo:repo-review. The README
leads with composition; reference material moved into docs/. All three
benchmarks re-measured, with every citing document updated.

CHANGELOG.md starts an in-repo record from this release on.