# Datasource: dev/test/integration-tests use Quarkus Dev Services for PostgreSQL.
# Production supplies QUARKUS_DATASOURCE_JDBC_URL / _USERNAME / _PASSWORD via environment.
quarkus.datasource.db-kind=postgresql
# Flyway owns the schema; Hibernate only validates
quarkus.flyway.migrate-at-start=true
quarkus.hibernate-orm.schema-management.strategy=validate
quarkus.hibernate-orm.physical-naming-strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
# Health
%prod.quarkus.management.enabled=true
# OIDC: dev/test use Keycloak Dev Services.
# Production supplies QUARKUS_OIDC_AUTH_SERVER_URL / _CLIENT_ID / _CREDENTIALS_SECRET via environment.
# Keycloak Dev Services on macOS/Docker Desktop: the host->container hop is classified as "external",
# so Keycloak demands HTTPS. Two layers are needed:
# - disable-https lets Dev Services reach the master realm's admin API over HTTP to provision.
# - the imported realm sets sslRequired=none so the app's runtime OIDC discovery over HTTP is accepted
# (the auto-created realm would otherwise default to sslRequired=external and 403 with "HTTPS required").
# The realm file also ships the quarkus-app client + alice/bob users.
%dev,test.quarkus.keycloak.devservices.disable-https=true
%dev,test.quarkus.keycloak.devservices.realm-path=quarkus-realm.json
%dev,test.quarkus.oidc.client-id=quarkus-app
%dev,test.quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
# Without these scopes the ID token carries only the subject: no preferred_username (dev
# adopt-username can never match, every login lands on onboarding), no name, no email.
quarkus.oidc.authentication.scopes=profile,email
# No RP-Initiated Logout: kanidm does not advertise an end_session_endpoint, and setting
# quarkus.oidc.logout.path makes Quarkus validate that endpoint at startup and abort boot when
# it is missing. Logout is therefore local session only (handled by the app).
# PKCE: kanidm enforces PKCE for the authorization code flow. The code_verifier rides through the
# redirect inside the state cookie, which must be encrypted with a stable, explicitly-set secret so
# every pod agrees on the key (otherwise a callback can land on a pod that cannot decrypt the cookie).
# Production supplies QUARKUS_OIDC_AUTHENTICATION_STATE_SECRET (>= 32 chars) via environment.
# token-state-manager.encryption-secret protects the post-login session cookie holding the tokens;
# it falls back to credentials.secret, but set it explicitly for multi-pod deployments.
quarkus.oidc.authentication.pkce-required=true
quarkus.oidc.authentication.state-secret=${QUARKUS_OIDC_AUTHENTICATION_STATE_SECRET:}
quarkus.oidc.token-state-manager.encryption-secret=${QUARKUS_OIDC_TOKEN_STATE_ENCRYPTION_SECRET:}
%dev,test.quarkus.oidc.authentication.state-secret=dev-only-oidc-state-secret-0123456789
# Behind the K3s ingress: trust X-Forwarded-* so OIDC builds the external https redirect_uri.
quarkus.http.proxy.proxy-address-forwarding=true
quarkus.http.proxy.allow-x-forwarded=true
quarkus.oidc.authentication.force-redirect-https-scheme=true
%dev,test.quarkus.oidc.authentication.force-redirect-https-scheme=false
# Session continuity (issue #6): kanidm issues short-lived (~15 min) ID tokens; without
# refresh-expired the session cookie is invalidated on expiry and the user is silently logged out.
# Instead, refresh with the stored refresh token — inline, no redirect, invisible to the user, and
# form POSTs survive. kanidm's refresh token currently lives a hard-coded 16 h (kanidm#3040), so
# keep the session cookie usable for 12 h; refresh proactively 60 s before expiry rather than on
# the first failing request. The refresh token already rides in the encrypted session cookie
# (default keep-all-tokens strategy + token-state-manager.encryption-secret above).
quarkus.oidc.token.refresh-expired=true
quarkus.oidc.token.refresh-token-time-skew=PT60S
quarkus.oidc.authentication.session-age-extension=PT12H
# Fixed code-flow callback (kanidm matches redirect_uri strictly — register <origin>/login there);
# after the token exchange Quarkus restores the originally requested URL, so deep links into
# protected pages survive a re-login, and direct /login?redirect=… visits keep their parameter.
quarkus.oidc.authentication.redirect-path=/login
quarkus.oidc.authentication.restore-path-after-redirect=true
# Issue/MR descriptions and review comments are posted as single form fields; the Vert.x default
# per-attribute cap of 2 KB answered 413 for anything longer than a short paragraph.
quarkus.http.limits.max-form-attribute-size=256K
# Dev-only convenience (see DevDataSeeder / UserProvisioningService):
# - seed-data: on dev startup, create user alice + a public "demo" repo with one commit (idempotent).
# - adopt-username: let a login adopt the seeded same-username row even though Keycloak Dev Services
# mint a fresh OIDC subject each run. Both default false; never enable in production.
%dev.gitshark.dev.seed-data=true
%dev.gitshark.dev.adopt-username=true
# Pages that require a logged-in user (git transport does its own auth). The runner Connect
# endpoints under /api/actions/* stay public: runners authenticate with their own uuid/token.
quarkus.http.auth.permission.authenticated.paths=/settings/*,/repos/new,/login,/onboarding,/following,/following/*,/admin,/admin/*
quarkus.http.auth.permission.authenticated.policy=authenticated
# Instance administrators: comma-separated handles allowed into /admin/* (runner management, etc.).
# Empty in production unless set; dev/test grant the seeded alice for convenience.
gitshark.admin.handles=${GITSHARK_ADMIN_HANDLES:}
%dev,test.gitshark.admin.handles=alice
# Native image: JGit holds static Random/lazy state that must not be build-time initialized
quarkus.native.additional-build-args=--initialize-at-run-time=org.eclipse.jgit,--initialize-at-run-time=org.apache.sshd.common.random,--initialize-at-run-time=org.apache.sshd.common.util.security.bouncycastle.BouncyCastleEncryptedPrivateKeyInfoDecryptor
# Register BouncyCastle at build time: SSHD's runtime registrar (Security.addProvider via reflection)
# does not work in a native image, leaving host-key generation without the BC provider.
quarkus.security.security-providers=BC
# Git repository storage
gitshark.storage.root=${GITSHARK_STORAGE_ROOT:data/repositories}
%test.gitshark.storage.root=target/test-repositories
# User profile pictures (avatars): bytes stored on the filesystem, keyed by user id
gitshark.storage.avatars=${GITSHARK_AVATAR_ROOT:data/avatars}
%test.gitshark.storage.avatars=target/test-avatars
# Repository images: bytes stored on the filesystem, keyed by repository id
gitshark.storage.repo-images=${GITSHARK_REPO_IMAGE_ROOT:data/repo-images}
%test.gitshark.storage.repo-images=target/test-repo-images
# Embedded SSH server
gitshark.ssh.port=${GITSHARK_SSH_PORT:2222}
gitshark.ssh.host-key-path=${GITSHARK_SSH_HOST_KEY:data/ssh/host-key}
%test.gitshark.ssh.port=0
%test.gitshark.ssh.host-key-path=target/test-ssh/host-key
# Push mirrors — replicate a repository to an external remote on every push.
# secret-key encrypts mirror credentials/SSH keys at rest; without it mirrors cannot be created
# (fail closed). max-attempts bounds the retry budget per sync (exponential backoff, then
# dead-letter). allow-insecure is dev/local ONLY: permits http:// and loopback/private mirror
# targets so two local instances can mirror to each other in tests.
gitshark.secret-key=${GITSHARK_SECRET_KEY:}
gitshark.mirror.max-attempts=${GITSHARK_MIRROR_MAX_ATTEMPTS:8}
gitshark.mirror.allow-insecure=${GITSHARK_MIRROR_ALLOW_INSECURE:false}
# Dev/test convenience (same pattern as the fixed dev OIDC secrets): a fixed key so mirrors work
# out of the box, and insecure targets allowed so a local instance can mirror to localhost.
# Production has no default — set GITSHARK_SECRET_KEY explicitly.
%dev,test.gitshark.secret-key=dev-only-mirror-secret-key-0123456789
%dev.gitshark.mirror.allow-insecure=true
%test.gitshark.mirror.allow-insecure=true
# Federation (ActivityPub / ForgeFed) — disabled by default.
# base-url is the public origin of this instance (e.g. https://shark.example); actor IDs are
# absolute and permanent once published, so it must be a real, non-loopback URL when enabled.
# peer-allowlist is a comma-separated list of peer hosts; empty denies all remote peers.
gitshark.federation.enabled=${GITSHARK_FEDERATION_ENABLED:false}
gitshark.federation.base-url=${GITSHARK_FEDERATION_BASE_URL:}
gitshark.federation.peer-allowlist=${GITSHARK_FEDERATION_PEER_ALLOWLIST:}
gitshark.federation.delivery.max-attempts=${GITSHARK_FEDERATION_MAX_ATTEMPTS:8}
# Dev/local ONLY: allow http + loopback/private federation targets (allowlist still enforced).
gitshark.federation.dev-allow-insecure=${GITSHARK_FEDERATION_DEV_ALLOW_INSECURE:false}
%test.gitshark.federation.enabled=true
%test.gitshark.federation.base-url=https://shark.test
%test.gitshark.federation.peer-allowlist=peer.test,shark.test