Private service bindings
Independently authored services can export a callable contract and grant named callers access through DI. Callers invoke operations on a binding name; they do not configure a URL or an exposed HTTP endpoint.
The runtime is in-process. invoke() is a same-process method call on a registered export. There is no RPC, HTTP, or wasmCloud hop in this package. Use the same decorator API in local development and tests; map targets with configuration or LocalServiceDevManager instead of changing caller code.
This is distinct from JSON-RPC and gRPC (typed request/response over a transport) and from wasmCloud native bindings (Postgres, KV, messaging, and similar host capabilities).
Installation
Canonical imports come from @di-framework/core/service-bindings. Decorators are also re-exported from @di-framework/core/decorators.
Checkout calling inventory
The checkout-inventory example is the runnable walkthrough.
1. Declare the contract
2. Export operations on the target
ExportService('inventory-service') is equivalent to { name: 'inventory-service' }. operations lists the methods bound callers may invoke. If it is omitted, the runtime discovers prototype function names (not instance arrow fields). Declare operations explicitly when the export set should be narrower than every method.
3. Inject a named binding on the caller
Injection always yields a proxy. Authorization, missing targets, and contract mismatches are checked at invocation, not at construction.
4. Grant access and run both services locally
There is no filesystem auto-discovery. Register exports and grants explicitly:
From the example package:
Configuration and target selection
ServiceBindingRuntime.current.configure() maps the current service's binding names to targets and records grants. Environment can change the target without changing caller code:
bindings may also be { inventory: { target, allowedOperations, mock } }. callers maps { [callerId]: { [bindingName]: CallerBindingConfig } } when one runtime hosts several callers.
Constructor defaults and environment:
Variable | Role |
|---|---|
| Current service id (default |
| Environment (else |
| JSON object mapping binding names for the current service |
| JSON array of grants; entries with |
Invalid JSON logs a warning and is ignored. A grant of caller: '*' authorizes every caller of that target. requiresAuthorization: false on @ExportService skips grant checks for that export.
LocalServiceDevManager forces environment = 'development'.
Status, reload, and mocks
API | Behavior |
|---|---|
| Reports |
| Emits |
| Flip the export between |
| Re-register the export under the same name; existing proxies keep working |
| Serve a mock without changing caller classes |
| Clear the manager / |
Mocks skip grant checks unless setEnforceAuthorizationOnMocks(true).
For tests, register a mock on the DI token instead of going through the manager:
serviceBindingToken(bindingName, caller?) is service-binding:${caller}:${bindingName} when a caller is supplied, otherwise service-binding:${bindingName}. Values with $bindingMeta are treated as real proxies and are not used as mocks.
Errors and troubleshooting
All errors extend ServiceBindingError and include a code plus details.remediation.
Class |
| When |
|---|---|---|
|
| No grant from caller to target while authorization is on |
|
| Binding is not mapped and no export uses that name |
|
| Export is missing or |
|
| Operation is not in the export set, or a mock lacks the method |
|
| Grant exists but |
Symptom | Likely cause |
|---|---|
| Missing |
| Binding name never mapped and no matching |
|
|
| Calling a method not listed in |
Mock ignored | Caller-scoped token does not match the invoke caller, or the value is a real proxy |
expectedOperations on @ServiceBinding and timeoutMs/required on binding options are stored on the decorator; the runtime does not validate or enforce them today.
Local versus deployed
Local ( | Deployed as implemented | |
|---|---|---|
Process | One Node/Bun process | Same in-process runtime |
Grants |
|
|
Target selection |
|
|
Transport | Direct method call | Direct method call |
Status / reload | Manager APIs | None beyond |
wasmCloud, the CLI, and @di-framework/rpc do not map @ExportService/@ServiceBinding onto independently deployed components. A wasmCloud workload can still use this API inside one component.
Next steps
Remote actors - Cross-process actor RPC and ownership (a different API)
RPC - Typed request/response over memory, HTTP, sockets, and gRPC
Testing - Mock substitution and unbound-caller tests
wasmCloud - Native host capability bindings (not this API)
CLI - Canonical command tree (no dedicated bindings command)