Skip to main content

arkitekt-service

What a service of an Arkitekt hub is made with. Three parts, for three different things a service process does:

arkitekt_service.contract What the service's image answers the hub's installer: what it needs, its own config written from the hub's facts, its migrations and upgrades.
arkitekt_service.service What the service is to the hub's rekuest: the structures it hosts and the signals it emits.
arkitekt_service.hook What can be done in its process: a HookAgent, whose actions rekuest reaches over HTTP.

They share arkitekt_service.trust — no secrets between services: every instance signs with its own key and the hub vouches for the public halves — and nothing else. A process uses any of them without the others.

pip install arkitekt-service

The contract

Every service image answers the same entry point:

python -m arkitekt_service describe    # what it needs from a hub and offers to it (JSON)
python -m arkitekt_service render      # this release's config, from the hub's facts
python -m arkitekt_service check       # does this release read a config as written?
python -m arkitekt_service migrate     # its database migrations, as a step
python -m arkitekt_service upgrade --from 5.2.0 --to 6.0.0

An installer (Konstruktor) then needs to know the hub, and nothing about a service that the service's image does not say. How a release spells its config, which keys it renamed, what it has to do to its data — all of that ships in the image, with the code it belongs to. Exit code 78 is a release's own refusal (facts it cannot be configured from, a setting it does not read), with the reason on stderr.

A container serves; a job prepares

A service's container does one thing: serve. Its start script starts the server and nothing else. Everything its database needs first is migrate: wait for the database, apply the release's migrations, then run the commands the service declared as its setup (an admin account, seeded rows), all in one process. Whoever starts the service runs it as a job of its own, once per build, before the first start and before an update's:

docker compose run --rm --no-deps mikro python -m arkitekt_service migrate

or, in a compose file that has no installer, as a service the server waits for:

mikro-migrate:
  image: jhnnsrs/mikro:7
  command: python -m arkitekt_service migrate
mikro:
  image: jhnnsrs/mikro:7
  depends_on:
    mikro-migrate:
      condition: service_completed_successfully

A container that merely restarts then does none of it. No run.sh migrates, seeds or waits for a database, and neither does a development one.

What describe prints also says how the image is run, so an installer assumes none of it:

  • identifier: what the service is registered as, and what a client asks for.
  • jobs.migrate: the command of the job above, or null for a service with nothing to prepare.
  • sidecars: what the service does not run without and ships as an image of its own (rekuest's takt), named from the service's image.
  • How it serves is not in there: that is the image's own CMD, which an installer leaves alone.

A service declares itself in one module, named by ARKITEKT_SERVICE in its Dockerfile:

from arkitekt_service.contract import JSON, Contract, Description, Facts, Needs, Offers, blocks


def render(facts: Facts) -> dict[str, JSON]:
    document = blocks.server(facts)  # django, postgres, redis, authentikate
    document["datalayer"] = blocks.datalayer(facts, "media", "zarr")
    return document


contract = Contract(
    description=Description(name="mikro", identifier="live.arkitekt.mikro", needs=Needs(storage=["media", "zarr"])),
    settings=Settings,
    render=render,
)

The hub's facts (arkitekt_service.contract.facts) and a service's description (arkitekt_service.contract.description) are versioned documents; an image refuses facts it does not understand rather than dropping them.

A service, and a hook agent

from arkitekt_service.service import Descriptor, Service, organization_of
from arkitekt_service.hook import HookAgent

service = Service("mikro", description="Microscopy data")
dataset = service.structure(ArrayDataset, "@mikro/arraydataset", descriptors=[Descriptor("@mikro/n_channels", "INT")], describe=array_descriptors)
service.model_signal(dataset, organization=organization_of())

agent = HookAgent("mikro", description="mikro's housekeeping")


@agent.action
def reembed_stale(organization: str) -> dict:
    """Re-embed stale rows."""
    ...


urlpatterns = [..., *service.urls, *agent.urls]

A service says what exists; an agent says what can be done. Neither knows the other.

Development

uv sync
uv run pytest                                          # the contract and the service
uv run pytest tests/hook --ds=hook_project.settings    # the hook agent: a process that is no service
uv run basedpyright

Releases are cut from conventional commits on main (tag-only, like the other Arkitekt packages) and uploaded to PyPI.

Metadata

Release files for arkitekt-service 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for arkitekt-service 1.3.0
File Size Uploaded
arkitekt_service-1.3.0.tar.gz 30.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for arkitekt-service 1.3.0
File Interpreter ABI Platform
arkitekt_service-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 70.7 kB

Release files / arkitekt_service-1.3.0.tar.gz

Download URL arkitekt_service-1.3.0.tar.gz
Size 30.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1cf4caa03eff5b6a09022c8058dd4e22d7baff8239a8b9b237d10e8dd47517e1
BLAKE2b-256 checksum
How to use checksums
8c7bd2ba8be3a82287970d8e24af7665d7fb0bb61b96d459699a4693d2ee9c4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / arkitekt_service-1.3.0-py3-none-any.whl

Download URL arkitekt_service-1.3.0-py3-none-any.whl
Size 39.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
07f759d7135005e295aee940ab2dd81e22b046bd33d3b0eadf8f4b71832452e0
BLAKE2b-256 checksum
How to use checksums
3fef1abaef277c5aac507e0d2089da3fade1e25de8ae5c9714570c451b5ef0fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

6.0.0

2 release files

5.2.1

2 release files

5.2.0

2 release files

5.1.0

2 release files

5.0.0

2 release files

4.0.0

2 release files

3.0.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

This release

1.3.0 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page