Files
computer-playbook/docs/requirements/023-setup-penpot.md
kevinveenbirkenbachandClaude Opus 5 f40d627a1b docs(agents): merge the tool-specific files into AGENTS.md
CLAUDE.md and GEMINI.md existed as per-runtime extensions of AGENTS.md, and
only one of them carried anything at all: GEMINI.md stated that it had no
additions, while CLAUDE.md held rules that bind every runtime anyway — the
permission announcement, the make-over-raw preference, the loop, chain and
heredoc bans, the stream-the-log-to-/tmp rule, the CI-evidence rule, the push
ban and the comment rule. A rule that binds every agent does not belong in one
agent's file. AGENTS.md absorbs them and now states that no tool-specific
extension file exists; agents whose runtime auto-loads a different filename
reach it through their own configuration instead.

The filenames were machinery as much as prose. The PR templates, the branch
and pull-request scope tables, the settings and security docs that justified
the loop deny by "the textual CLAUDE.md rule", six requirement documents whose
push instruction linked ../../CLAUDE.md, and a test docstring citing the
shell-loop ban all named them, and now name AGENTS.md.
scripts/meta/resolve/pr/scope.sh deliberately keeps matching CLAUDE.md and
GEMINI.md: this change deletes them, and without those patterns the change
itself would not classify as agent-scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 22:51:04 +02:00

9.5 KiB

023 - Setup Penpot

User Story

As a Infinito.Nexus platform operator, I want to provide a fully integrated, production-ready Penpot design and prototyping platform within the Infinito.Nexus ecosystem so that teams can create, collaborate on, and share design projects directly inside their sovereign Infinito.Nexus installation.

Context

Penpot is an open-source design and prototyping platform (a self-hostable Figma alternative). This requirement tracks a new web-app-penpot Ansible role that deploys Penpot's containerized stack and wires it into the Infinito.Nexus ecosystem (identity, reverse proxy, marketplace, backup).

The role MUST follow the standard role-meta layout described in layout.md (meta/services.yml, meta/secrets.yml, meta/volumes.yml, meta/variants.yml, meta/info.yml, meta/main.yml). web-app-openproject is the closest structural model — it combines PostgreSQL, a Redis cache service, background workers, an internal proxy, Keycloak SSO and LDAP — and SHOULD be used as the primary reference; web-app-taiga and web-app-confluence are secondary references.

Penpot's upstream stack is frontend + backend + exporter + PostgreSQL + Redis. The exporter container is mandatory: it backs the export (SVG/PDF) and developer-handoff features required below.

Acceptance Criteria

Role & meta layout

  • A new role exists at roles/web-app-penpot/ and follows the role-meta layout in layout.md.
  • meta/services.yml declares every container service and a lifecycle key (starting tier beta).
  • meta/secrets.yml defines the role's configurable variables.
  • meta/volumes.yml declares the role's named volumes.
  • meta/variants.yml declares the matrix-deploy variants (see Variants below).
  • meta/info.yml and meta/main.yml are present and valid.

Compose stack

  • The stack includes a Penpot frontend container.
  • The stack includes a Penpot backend container.
  • The stack includes a Penpot exporter container (required for SVG/PDF export and developer handoff).
  • PostgreSQL is provided via svc-db-postgres (declared through the postgres service block, not a role-local database container).
  • Redis is provided via the redis service block in meta/services.yml (auto-emitted svc-db-redis sidecar; service name redis).
  • All container volumes, env vars, ports and healthchecks follow Infinito.Nexus conventions.

Identity integration

  • OIDC login is available via web-app-keycloak, enabled through PENPOT_FLAGS (e.g. enable-login-with-oidc). (verified live against a Penpot + Keycloak deploy.)
  • LDAP login is available via svc-db-openldap, enabled through PENPOT_FLAGS (e.g. enable-login-with-ldap). (verified live against a Penpot + OpenLDAP deploy.)
  • Native local email/password login is off under OIDC (disable-login-with-password forces users through SSO) and on otherwise, derived from the sso service flag (variant-aware); when on, the role bootstraps a local password for the administrator profile via the backend PREPL (enable-prepl-server) in tasks/main.yml, and the bootstrap is skipped under OIDC.
  • Self-registration is on when OIDC is enabled (Penpot's OIDC JIT provisioning routes through the registration path) and off otherwise, derived from the sso service flag, rendering enable-registration/disable-registration in PENPOT_FLAGS.
  • OIDC and LDAP configuration is fully automated via Ansible (env vars / templated config), requiring no manual post-deploy steps.

CSP & reverse proxy

  • The role generates a correct Nginx/CSP vhost using csp_filters.py and the nginx_vhost logic (via sys-stk-full + meta/server.yml CSP flags).
  • The vhost configures WebSocket upgrade so real-time collaboration (live cursors, comments) works (handled by the shared proxy template).
  • The vhost supports HTTPS termination and routes to internal service names.

Variants

  • meta/variants.yml defines an all-on variant pinning every dynamic services flag to true.
  • meta/variants.yml defines an all-off variant pinning every dynamic services flag to false.
  • meta/variants.yml defines an LDAP-only variant (ldap on, sso/OIDC off) per the convention in web-app-openproject/meta/variants.yml.

Playwright coverage

  • A per-role Playwright spec exists and gates authenticated scenarios via skipUnlessServiceEnabled per 006 - Service-gated Playwright tests.
  • A separate OIDC login scenario and LDAP login scenario exist (not combined into one test body) per 018.
  • Both the canonical admin persona and the non-admin RBAC persona biber are exercised per 017.
  • A native (local-password) admin login scenario exists (test-login-native.js), separate from the OIDC/LDAP companions, and is skipped when sso is enabled (native login disabled under OIDC).
  • A scenario verifies project creation.
  • A scenario verifies asset upload.

Infinito.Nexus integration

  • A marketplace entry for "Penpot Design" is added and correctly categorized (auto-discovered web-app role with meta/info.yml + galaxy_tags).
  • The role declares menu tags: design, penpot, prototyping, collaboration, ui-ux, figma-alternative.
  • Desktop and mobile launcher URLs are generated automatically from the role's domain.
  • PostgreSQL data is backed up via the existing backup role(s).
  • The asset-storage volume is backed up via the existing backup role(s).

Storage

  • A named volume is configured for asset storage (design files, images, fonts) and is backup-ready.
  • Volume mounts follow Infinito.Nexus conventions.
  • S3-compatible object storage is documented as a future option in the role README (not implemented).

Definition of done

  • Penpot deploys successfully with one command on a fresh host.
  • The app is reachable at its generated canonical domain (penpot.design.{{ DOMAIN_PRIMARY }}, e.g. penpot.design.infinito.nexus).
  • roles/web-app-penpot/README.md documents the role's purpose, configuration, and the S3 future option.
  • make test passes with the new role in place. (Penpot-relevant lint + integration suites verified green on host; full in-container make test requires make setup/sudo not available in the sandbox.)

Procedure

The implementation of this requirement MUST be executed autonomously by the agent. The following rules apply for the entire run and are non-negotiable:

  • Clarifying questions only at the start. Any open question, ambiguity, or missing decision (e.g. Penpot image versions, exporter resource limits, PENPOT_FLAGS set, lifecycle starting tier, secrets source) MUST be raised once at the very beginning of the run, in a single batched round, BEFORE any file is changed. Ambiguities discovered mid-run MUST be resolved by the agent using its best judgement, recorded in the role's README.md or a code comment, and revisited only at PR review.
  • Iteration loops. The agent MUST follow the Compose Loop for every change inside roles/web-app-penpot/, and the Playwright Loop for every change to the role's Playwright spec. The debug-locally step MUST NOT be skipped in favour of remote CI reruns.
  • No ask prompts mid-run. The agent MUST NOT trigger any tool call that routes through permissions.ask in .claude/settings.json during implementation. Where a tool would otherwise route through ask, the agent MUST select an equivalent covered by permissions.allow, or rephrase the operation to fit the sandbox. The single permitted exception is the final commit.
  • No interruptions. Bug fixes, deploy failures, lint failures, make test failures, and healthcheck flaps MUST be resolved at their root inside this same iteration without prompting the operator. Workarounds, ad-hoc skips, retry-until-green loops, or "track in a follow-up" deferrals MUST NOT be used.
  • One commit at the end. ALL changes (role, meta, Playwright spec, documentation, marketplace entry, and the ticked checkboxes in this document) MUST be combined into ONE commit, created only after every Acceptance Criterion above is checked off (- [x]) and make test is green. The agent MUST NOT push; the operator runs git-sign-push outside the sandbox per AGENTS.md.

See Also