Service

Function pools, promoted to network services — typed methods, a universal dynamic client, Kibo-generated typed proxies.

What is a Viper C++ Service?

A Service is a Viper C++ Runtime container that exposes typed business logic over a network socket. It composes DSM definitions and one or more function pools — stateless or stateful — into a single immutable bundle, then serves them through Viper C++'s RPC layer with automatic marshalling. Clients connect, discover the typed surface from runtime metadata, and call typed methods.

Stateful pool methods read and write through an abstract state interface — the Service mechanism has no opinion on what backs it. It does not persist anything and does not assume any storage tier ; stateful pools work over any backend the client provides (in-memory, plain database, or any other receptacle).

DSM
shape + pool signatures
Kibo
typed code
Viper C++
runtime, metadata, RPC
Service
typed network surface

The Kibo step uses the kibo-template-viper pack — the template that produces the typed surface of the Dual Reality (typed Python wrappers, typed C++ proxies). Kibo itself is template-agnostic.

Built on DSM-defined types, Kibo-generated bridges, and Viper's Metadata Everywhere ; the surface a Service exposes is typed, network-callable business logic.

Anatomy of a Service

A Service is not a separate stack — it is one more per-app surface on top of the shared code base a product already ships through its UI shells. Only layer 1 is per-app ; everything below is the same code, written and maintained once.

Viper C++ Service

1 per-app surface · 5 shared layers

1
Network Surface (RPC) Typed methods exposed over a socket — clients discover pools from runtime metadata
per-app
Shared code base
2
Service Host (Application Context) Owns the pool instances and dispatch — composes DSM and pools into one immutable bundle
3
Bridge Layer Pool bridges route generated pool calls into hand-written business logic
4
Business Logic (C++) Hand-written pool methods — pure (FunctionPool) or stateful (AttachmentFunctionPool)
5
Generated by Kibo Data classes, attachments, definitions, function pools, marshalling
6
Viper C++ Runtime Type · Value · Database · Commit · Serialization · RPC

A Service is one more layer-1 surface on a shared core — same anatomy as the Commit Application Model, with a socket on top instead of a UI.

Two pool types

Two flavours of typed pool, declared in DSM and exposed by the Service.

FunctionPool

stateless · pure

Groups stateless functions executed on the server. Pure inputs to outputs, no access to storage, no side effects. Type-safe through the generated prototype.

function_pool Tools {...} {
    int64 add(int64 a, int64 b);
    Vector3 addVector(Vector3 a, Vector3 b);
};

AttachmentFunctionPool

stateful · over an abstract state interface

Groups functions that operate over a state interface passed at call time by the client. mutable methods are explicitly marked. Backend-neutral by construction.

attachment_function_pool PlayerModel {...} {
    mutable key<Player> create(string nickname, Level level);
    optional<key<Player>> has_player(string nickname);
};

Two paths to a service — Dual Reality

Same RPC layer underneath, two ways to consume it. The split is the Dual Reality pattern : an adapter between Viper's dynamic runtime (strongly typed via its metadata catalog) and the static API world where developers and IDEs are most productive. Both sides are typed ; the static surface adds editor ergonomics, not type safety.

static · per-pool typed

Kibo-generated typed proxies

For IDE autocompletion and compile-time type checking, Kibo emits per-pool typed proxies — function_pool_remotes.py and attachment_function_pool_remotes.py in Python, *_FunctionPoolRemotes.hpp in C++ :

from service.function_pool_remotes import Tools

TOOLS = Tools(service_remote)
if TOOLS.is_available():
    result = TOOLS.add(32, 10)        # typed, autocompleted

The typed proxies are thin wrappers over the same dynamic dispatch — both paths are always available, the application picks per call site.

dynamic · universal

Generic universal client

ServiceRemote.connect(...) returns a handle that introspects the service's pools at connect time. Calls dispatch through a runtime dictionary :

from dsviper import Definitions, ServiceRemote

defs = Definitions()
s = ServiceRemote.connect("localhost", "54328", defs)

result = s.function_pool_funcs("Tools")["add"](32, 10)

One client mechanism for any service, ever. No per-service module, no compile-time schema, no codegen to install. The five-line scaffold ships with dsviper-tools as service_client.py.

Remote-Local — business on server, mutations on client

For stateful pools, the protocol does something unusual : the business logic executes on the server, but every state mutation lands in the client's local AttachmentMutating. The roles invert during execution.

CLIENT
SERVER
PM.create(mutating, "shadow", BEGINNER)
Execute business logic
CallAttachmentMutatingUpdate server asks client to mutate
role inversion
Mutate CLIENT-LOCAL AttachmentMutating
ReturnVoid
ReturnValue (final result)

On the server, the AttachmentMutating the pool method receives is an AttachmentMutatingRemote proxy that forwards every read and write back to the client — fifteen callback packet types cover the surface. The business code never knows it is talking to a remote state.

Safety by isolation

The Remote-Local pattern looks dangerous — the server sends commands that mutate client state. The design is inherently safe : the server has no handle on the client's authoritative state. Mutations land in an AttachmentMutating context owned by the client, isolated from the model the client treats as ground truth. The client alone decides whether — and when — to integrate that context back into its authoritative state.

Failure mode Effect on the client's authoritative state
Network timeout Mutation context abandoned, never integrated
Server exception Mutation context abandoned, never integrated
Server sends invalid data Client may discard the context before integration
Partial mutations Context never integrated as a whole

The server only manipulates a remote-mutating proxy ; it has no view of the client's authoritative state and could not write to it if it wanted to. Structural integrity at the protocol boundary is guaranteed by this isolation. Semantic integrity under multi-author reduction is a separate concern of the commit layer — see the Dual-Layer Contract.

Promote your business logic to a service

The Service Architecture page covers the full mechanism — pool types, server side, both client paths, the Remote-Local protocol, and the safety story end to end.