Skip to main content
A Uniac package is either a Service or a System. Both subclass marker classes from the uniac SDK and declare their dependencies as class-level type annotations.
KindSDK base classPurpose
libuniac.ServiceReusable building block, consumed by other Services or Systems.
appuniac.SystemDeployable composition. Each deploy targets one System.
The kind field in uniac.json selects which one applies.

Declaration shape

A Service or System is a plain Python class. Class-level annotations of type Node declare what it depends on; __init__ initializes them.
from uniac import System, load
from db import Db        # generated stub for a published Service
from cache import Cache  # generated stub for a published Service

class Api(System):
    db: Db
    cache: Cache

    def __init__(self, log_level: str = "info"):
        self.db = Db()
        self.cache = Cache()
        self.log_level = log_level

service = load(Api)
load() constructs the instance, validates that every annotated Node slot is filled, and hydrates each Node’s .url from the deploy-time wiring. See load.

Service vs System: when to use which

  • Use Service (kind=lib) for code you want other packages to consume — a database client, a logger, a feature flag reader. Publish it with uniac build.
  • Use System (kind=app) for the unit you deploy. A System pulls Services together and exposes the public surface (HTTP, jobs, scheduled work).
A Service can depend on other Services. A System can depend on Services. Services never depend on Systems.

No magic

The framework does not auto-instantiate annotated Nodes. The annotation is a declaration; the value comes from your __init__. load() validates after construction and raises UniacValidationError if any slot is unset or holds the wrong type. There is no base-class __init__ to call. If you don’t need init args, omit __init__ entirely.