Skip to content
Alvo is pre-v0.1: the image runs from its edge tag, and no NuGet package or release is published yet.Pre-v0.1: no release yet.Roadmap and status

Run your own descriptor

Run the published standalone image over a descriptor you wrote, with API keys for the roles it declares, using Docker and three downloaded files.

  • Docker with Compose v2, curl and openssl, and port 8080 free. If the Quick start’s stack is still running, stop it first: both listen on 127.0.0.1:8080.
  • A descriptor file. If you do not have one yet, the commands below use examples/help-desk, and Start from an example takes examples/simple-tasks as a starting point.

The quick start’s docker-compose.quickstart.yml defines one dev key, demo. Its roles are the built-in admin and authenticated, so it authenticates against any descriptor, but a key with one role the descriptor does not declare authenticates nothing: every request with it is a 401 unauthenticated. A key’s roles must be listed in the descriptor’s auth.roles or be built in (authenticated and admin are). So declare keys for your own roles in a compose override, a second file that compose merges over the first:

docker-compose.override.yml
# Saved as docker-compose.override.yml next to docker-compose.quickstart.yml. With
# COMPOSE_FILE=docker-compose.quickstart.yml:docker-compose.override.yml exported, every `docker compose`
# command merges it over the quick start's stack. It serves ./help-desk.alvo.json instead of an example
# inside the image, runs as its own project (its own database), and adds two dev keys for examples/help-desk
# beside the quick start's demo key (index 0). Every role a key carries must be declared in the descriptor's
# auth.roles or be built in (authenticated, admin): a key with one undeclared role authenticates nothing.
name: alvo-help-desk
services:
alvo:
environment:
Alvo__DescriptorPath: /alvo/descriptor.json
Alvo__Auth__DevKeys__1__KeyId: agent
Alvo__Auth__DevKeys__1__Secret: ${ALVO_AGENT_KEY_SECRET:?set ALVO_AGENT_KEY_SECRET before starting the stack}
Alvo__Auth__DevKeys__1__User: 3f2b8c1e-7a4d-4e9b-9c21-5d6e7f8a9b01
Alvo__Auth__DevKeys__1__Roles__0: agent
Alvo__Auth__DevKeys__1__Roles__1: authenticated
Alvo__Auth__DevKeys__1__Scopes__0: "*:read"
Alvo__Auth__DevKeys__1__Scopes__1: "*:write"
Alvo__Auth__DevKeys__2__KeyId: admin
Alvo__Auth__DevKeys__2__Secret: ${ALVO_ADMIN_KEY_SECRET:?set ALVO_ADMIN_KEY_SECRET before starting the stack}
Alvo__Auth__DevKeys__2__User: 8d4e2a6c-1b3f-4c5d-8e7a-9f0b1c2d3e02
Alvo__Auth__DevKeys__2__Roles__0: admin
Alvo__Auth__DevKeys__2__Roles__1: authenticated
Alvo__Auth__DevKeys__2__Scopes__0: "*:read"
Alvo__Auth__DevKeys__2__Scopes__1: "*:write"
volumes:
- ./help-desk.alvo.json:/alvo/descriptor.json:ro

It adds two keys for help-desk, whose auth.roles are admin and agent: agent (roles agent, authenticated) and admin (roles admin, authenticated). Change the KeyIds and roles to your descriptor’s. Each secret is read from a variable, and the :? form makes compose refuse to start rather than invent one; a secret must be at least 32 characters.

The override also mounts ./help-desk.alvo.json read-only at /alvo/descriptor.json and points the host at it, so the stack serves your file instead of an example inside the image. To serve a file with another name, change the left side of that volumes line. name: alvo-help-desk gives this stack its own database, apart from the quick start’s.

  1. Make a directory for the stack, download the compose file, the override and the descriptor, generate the secrets, and start it:

    Terminal
    mkdir -p alvo-help-desk && cd alvo-help-desk
    curl -fsSLO https://raw.githubusercontent.com/Burgyn/MMLib.Alvo/main/docker-compose.quickstart.yml
    curl -fsSL -o docker-compose.override.yml https://raw.githubusercontent.com/Burgyn/MMLib.Alvo/main/website/src/snippets/shell/help-desk.compose.override.yml
    curl -fsSL -o help-desk.alvo.json https://raw.githubusercontent.com/Burgyn/MMLib.Alvo/main/examples/help-desk/help-desk.alvo.json
    export COMPOSE_FILE=docker-compose.quickstart.yml:docker-compose.override.yml
    export ALVO_DEMO_KEY_SECRET="$(openssl rand -hex 16)" ALVO_ADMIN_PASSWORD="$(openssl rand -hex 12)"
    export ALVO_AGENT_KEY_SECRET="$(openssl rand -hex 16)" ALVO_ADMIN_KEY_SECRET="$(openssl rand -hex 16)"
    docker compose up --wait --wait-timeout 90
    curl -sS localhost:8080/api/tickets -H "X-Alvo-Api-Key: agent.$ALVO_AGENT_KEY_SECRET"

    COMPOSE_FILE names both files, so every plain docker compose command in this shell uses them together. --wait returns once the API is ready, which means the descriptor is applied. The last command lists the tickets with the agent key: an empty page, because nothing has been written yet.

  2. Every compose command reads both files again, down included, so keep these variables exported in the shell you use for the stack, or put them in a .env file next to the compose files, which compose reads by itself.

The API is at http://localhost:8080/api/<entity>, its OpenAPI document at /openapi/v1.json, an API browser at /scalar, and the dashboard at /admin, where you sign in as admin@alvo.local with ALVO_ADMIN_PASSWORD. The Tutorial walks through this exact stack one change at a time.

Edit help-desk.alvo.json, then recreate the alvo container so it reads the file again:

Terminal
docker compose up -d --wait --force-recreate alvo

On start, Alvo compares the descriptor with the schema it applied before. A change that discards nothing (a new entity, a new field, a changed rule) is applied; one that would drop or narrow data is refused, and the container stops with the plan in its log. To apply without a restart, send the descriptor to the Management API instead; both paths, with dry runs, revisions and rollback, are in Apply and evolve your descriptor. In this build everything but a new entity takes effect that way at once; a new entity gets its Data API route after a restart (#103).

examples/simple-tasks is the smallest real backend: projects and the tasks inside them, owned by the user who created them. Download it over the mounted file, and from then on it is your descriptor:

Terminal
docker compose down --volumes
curl -fsSL -o help-desk.alvo.json https://raw.githubusercontent.com/Burgyn/MMLib.Alvo/main/examples/simple-tasks/tasks.alvo.json
docker compose up --wait --wait-timeout 90
curl -sS localhost:8080/api/projects -H "X-Alvo-Api-Key: admin.$ALVO_ADMIN_KEY_SECRET"

The first line deletes the stack’s database. A database holds the schema of the descriptor it served; starting a different descriptor over it would drop the old tables, and Alvo refuses that (see What can go wrong).

simple-tasks declares no auth.roles, so of the override’s keys only admin works there (its roles are both built in, like the demo key’s); the agent key answers 401 until you add "roles": ["agent"] to the descriptor’s auth block, or remove agent from the key.

Stop the stack and delete its database, in the same shell. The unset makes a plain docker compose in this shell stop reaching for the override; the quick start’s commands name their file with -f anyway:

Terminal
docker compose down --volumes
unset COMPOSE_FILE
Run the host with the .NET SDK and SQLite instead

The same descriptor and the same two keys, on the host process directly, from a clone of the repository (git clone https://github.com/Burgyn/MMLib.Alvo; run the commands in its root). You need the .NET SDK the repository’s global.json pins. The database is a SQLite file in your temporary directory, and the host listens on the address the stack uses, so stop the stack first.

Terminal
export ALVO_DESCRIPTOR="$PWD/examples/help-desk/help-desk.alvo.json"
export ALVO_AGENT_KEY_SECRET="$(openssl rand -hex 16)"
export ALVO_ADMIN_KEY_SECRET="$(openssl rand -hex 16)"
openssl rand -base64 24 > .alvo-admin-password
dotnet run --project src/MMLib.Alvo.Host -- \
--environment Development --urls http://127.0.0.1:8080 \
--Alvo:DescriptorPath="$ALVO_DESCRIPTOR" \
--Alvo:Database:Provider=sqlite \
"--Alvo:Database:SqliteConnectionString=Data Source=${TMPDIR:-/tmp}/alvo-help-desk.db" \
--Alvo:Admin:BootstrapEmail=admin@example.com \
--Alvo:Admin:BootstrapPasswordFile="$PWD/.alvo-admin-password" \
--Alvo:Auth:DevKeys:0:KeyId=agent --Alvo:Auth:DevKeys:0:Secret="$ALVO_AGENT_KEY_SECRET" \
--Alvo:Auth:DevKeys:0:User=3f2b8c1e-7a4d-4e9b-9c21-5d6e7f8a9b01 \
--Alvo:Auth:DevKeys:0:Roles:0=agent --Alvo:Auth:DevKeys:0:Roles:1=authenticated \
"--Alvo:Auth:DevKeys:0:Scopes:0=*:read" "--Alvo:Auth:DevKeys:0:Scopes:1=*:write" \
--Alvo:Auth:DevKeys:1:KeyId=admin --Alvo:Auth:DevKeys:1:Secret="$ALVO_ADMIN_KEY_SECRET" \
--Alvo:Auth:DevKeys:1:User=8d4e2a6c-1b3f-4c5d-8e7a-9f0b1c2d3e02 \
--Alvo:Auth:DevKeys:1:Roles:0=admin --Alvo:Auth:DevKeys:1:Roles:1=authenticated \
"--Alvo:Auth:DevKeys:1:Scopes:0=*:read" "--Alvo:Auth:DevKeys:1:Scopes:1=*:write"

Ctrl+C stops it. A change to the descriptor applies on the next start, as with the stack.

StatusProblem typeWhenFixReturned by
401unauthenticatedThe key carries a role the descriptor does not declare (the agent key over a descriptor without agent), or the secret is wrong.Give the key only declared or built-in roles; export the same secret the stack was started with.every host

The stack does not start. docker compose up --wait fails and the alvo container has exited. Read why:

Terminal
docker compose logs alvo

The end of the log says what to change. The common reasons:

  • The descriptor is invalid. The log shows Descriptor validation failed: and one line per problem: its JSON pointer, for example /entities/tickets/fields/estimate_cost/computed, what is wrong and how to fix it. Correct the file and start again.

  • The change would discard data. The log says Alvo cannot start:, lists the plan with the destructive steps marked (such as DropEntity tickets), and the container exits with code 78. Keep what the descriptor dropped, or, if the data is disposable, start over with an empty database:

    Terminal
    docker compose down --volumes
  • A variable is missing. Compose itself refuses, naming the variable (set ALVO_AGENT_KEY_SECRET before starting the stack), before any container starts. Export it again.

Apply and evolve your descriptor: change a running backend safely, with dry runs, revisions and rollback.