Five ideas. Once you've got these, every other PMA page is just detail.
PMA is a thin framework that reads YAML and orchestrates a dozen
self-hosted services. The five concepts below name the moving parts.
You write the manifests. Bootstrap reads them and brings the
service up — including SSO registration with Authentik.
just release-run is the verb for atomic updates afterwards.
Recovery playbooks document every failure mode + its fix. The MCP
gateway exposes the whole operator surface to AI agents.
The container around everything one service needs. Each
packages/<svc>/ directory holds:
manifest.yaml — declares container name, ports, DB,net.manifest.yaml — declares the Caddy route + tunnel.asd-layer schema — see/asd/reference/net-manifest.)tpl.env — env-var template (with per-mode keys if needed).docker-compose.yaml — container definitions.install.just — service-specific just recipes.scripts/ — lifecycle hooks (pre_start_init,post_start_init, post_data_restore, ensure-config).Adding a 16th service means adding packages/<new-svc>/ with
these files. The framework picks it up — no framework code
changes.
# Minimal manifest.yaml example
name: redmine
display_name: Redmine
category: project_management
classification: enterprise
containers:
- name: redmine
role: app
health_check: true
sso:
type: oauth
redirect_path: /login/oauth2/callback
database:
type: postgresql
name: redmine
backup:
enabled: true
type: database
health:
endpoint: /
expected_code: 200
Reference: /pma/reference/manifest (planned).
The code that reads manifests and does the work. Lives in
scripts/, the top-level Justfile, the bootstrap orchestrator.
Contains no service-specific knowledge.
Rule (enforced via pre-commit hooks): if service == "redmine"
does not appear anywhere in framework code. The framework iterates
over manifest declarations; service-specific behaviour lives in
packages/<svc>/scripts/ hooks.
This is what makes "adding a service" cheap. The framework already
knows how to: install a service (read manifest), configure SSO
(read sso.type), set up backups (read backup.*), run health
checks (read health.endpoint), generate Caddy routes (read
net.manifest.yaml). Each new service inherits all of it.
Reference: /pma/internals/package-system (planned).
release-run and release-revertThe two verbs for production deploys. just release-run TICKET
does: fetch + pull + capture BASE_SHA + per-service backup +
regenerate migrate.sh from diff + run migrations + run per-ticket
script + verify. Halt-on-failure between every phase.
If anything fails, just release-revert TICKET restores the
backup taken in phase 1 and reverts the merge commit. Single
command rollback.
# Deploy
ssh prod 'cd /opt/asd-pma && just release-run 1234'
# Roll back
ssh prod 'cd /opt/asd-pma && just release-revert 1234'
Per Golden Rule 13: these are the only operator entry points
for a deploy. Don't pre-pull. Don't invoke the sub-scripts
directly. The orchestrator owns its own git state.
Reference: /pma/internals/release-orchestrator (planned).
One login, every service. sso.type: oauth (or oidc, saml,
proxy, wikijs, frappe) in a package's manifest.yaml
auto-integrates the service with the PMA Authentik instance
during bootstrap. The bootstrap creates the OAuth application,
registers the callback URL, configures the service's auth strategy
— all from the manifest.
Log in to Redmine → already logged in to Mattermost → already
logged in to ERPNext → … No per-service password to manage. User
provisioning is centralised; revoke an account in Authentik and
the user is locked out everywhere.
Reference: /pma/learn/04-sso-everywhere (planned) + /pma/internals/architecture (planned).
Same surface for humans and AI. PMA ships an MCP server
(claude_ai_PMA) that exposes structured tools for every major
operation: create Redmine tickets, search Zammad, trigger n8n
workflows, send mail, query service health, view logs.
An AI agent calls mcp__claude_ai_PMA__redmine_create_ticket —
the call lands in Redmine as a real ticket with proper attribution
to the agent (via X-Redmine-Switch-User). Audit trail in the
agent's own identity, not the admin user.
This isn't a hack on top — it's a designed-in operator surface.
Every just recipe has a corresponding MCP tool. Every command
emits --json when asked. Every package has a manifest contract
the agent can read.
Reference: /pma/internals/mcp-gateway (planned).
/pma/learn/00-start-here./pma/services./pma/internals.