Core concepts¶
The pieces¶
libpolycall — the reusable native library (lib/libpolycall.a /
.so / .dll). It implements configuration parsing, the runtime
lifecycle, the wire protocol, and the public C API. It has no
main() of its own.
polycall / polycall.exe — the operator program. A thin dispatcher
(cli/main.c) over a fixed command registry (cli/cli_registry.c); it
translates parsed command-line options into calls against libpolycall
and renders the result as human text or JSON. It contains no business
logic of its own — see the CLI reference.
The runtime — created and started by polycall runtime start (or an
embedding application calling polycall_runtime_create/_start). It
parses the Polycallfile, binds
runtime.listen, accepts connections, authenticates them, and routes
INVOKE requests to whichever registered provider declared that
service.method. It does not implement any service's logic itself.
A provider — an application process (or, via the C provider API, a
library user in the same style) that connects to the runtime, registers
one or more methods, and answers INVOKE requests for them. The Python
add(a, b) example and the C math_provider.c example are both
providers.
A binding — the language-specific glue that speaks the wire protocol
(or, for C, calls the public API directly) so an application in that
language can act as a caller or a provider. examples/python-math/provider.py
is a binding for Python, written out in full rather than packaged; there
is currently no installable per-language package (see
Language integration).
A caller — whoever issues the request: the polycall call invoke CLI
command, or an embedding application using polycall_call_submit.
Polycallfile — the single, explicit source of runtime topology and
policy: what's listening where, which services and methods exist and with
what typed signatures, who is authenticated, and what they're authorized
to do. See the Polycallfile reference.
Registration and method signature¶
Declaring [method.math.add] in the Polycallfile with
params = ["i64", "i64"] and result = "i64" only makes that method
invocable in principle. Nothing answers a call for it until a provider
actually connects and sends a matching REGISTER for math.add, and the
runtime's dispatcher accepts that registration (checking, among other
things, that the provider's declared signature matches the configured
one). Until then, call invoke reports unavailable — this is a real
routing state, not a bug in the demo.
Typed result, not "whatever JSON produces"¶
A method's params and result are one of a fixed set of wire types
(i64, bool, string, ...; see the
Polycallfile reference).
Arguments are checked against the declared types before a provider ever
sees them, and results are checked on the way back. This is why sending a
string where i64 is declared produces a structured INVALID_ARGUMENT
error rather than a provider crash, and why a 64-bit integer round-trips
exactly instead of being silently reinterpreted as a JavaScript-range
float.
Why the provider does not parse Polycallfile¶
provider.py needs the runtime's listen address and the shared
credential, both of which live in the Polycallfile. Rather than
implementing a second parser for that grammar, it runs
polycall.exe config show --json <Polycallfile>
as a subprocess and reads libpolycall's own validated JSON output. This
matches CFG-004:
schema 1's grammar is deliberately restricted specifically so that
independent adapters do not each grow their own, subtly different parser.
A provider written in any language should do the same — shell out to
polycall config show --json, not re-implement the grammar.
Local calls, cross-process calls, and HTTP are not interchangeable¶
- A direct C ABI call (
polycall_call_submitfrom an application linked againstlibpolycall) happens in-process. There is no network hop and no wire serialization; arguments arepolycall_value_thandles. - A cross-process protocol call (what
call invokeand every example provider use) goes over TCP, framed and encoded per LPV1-06: a length-prefixed JSON envelope carrying tagged values. This is what makes a Python provider reachable from a separately-running CLI process. - A plain HTTP request to some other port is neither of the above. If
an application also happens to expose an HTTP endpoint (the old trial
build's book-service demo did — see the
migration guide), a
successful
curl/http.clientrequest to it proves nothing about whether LibPolycall routed anything. Nothing in the current implementation bridges HTTP requests into the runtime's dispatcher.
Security terms, used precisely¶
Authentication answers "is this connection who it claims to be" — the
runtime checks a shared credential (local-auto, generated and stored via
the OS credential facility, or token, from a named environment
variable) during HELLO/AUTH. Authorization answers "is this
authenticated principal allowed to do this" — checked per-request
against [grant.*] entries (actions = ["invoke", "register"],
constrained to specific service/methods). These are two different
checks; a connection can authenticate successfully and still have a
specific invocation or registration denied. See
Security and telemetry for what's
actually implemented versus proposed.