9.7 KiB · text 5af55d9
# This file is responsible for configuring your application
# and its dependencies with the aid of the Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.
# General application configuration
import Config
config :git_gud, :scopes,
user: [
default: true,
module: GitGud.Accounts.Scope,
assign_key: :current_scope,
access_path: [:user, :id],
schema_key: :user_id,
schema_type: :id,
schema_table: :users,
test_data_fixture: GitGud.AccountsFixtures,
test_setup_helper: :register_and_log_in_user
]
config :git_gud,
ecto_repos: [GitGud.Repo],
generators: [timestamp_type: :utc_datetime],
# Registration mode. One of: :open, :invite_only, :closed.
# Defaults to :invite_only — safer for fresh deploys. Override in
# runtime.exs / per-env config (use :open for unrestricted signups).
registration_mode: :invite_only
# Oban for background jobs (cache backfill, webhook delivery, CI dispatch,
# federation retention).
config :git_gud, Oban,
engine: Oban.Engines.Basic,
repo: GitGud.Repo,
queues: [
default: 10,
git_cache: 4,
webhooks: 8,
ci: 4
],
plugins: [
{Oban.Plugins.Cron,
crontab: [
{"@daily", GitGud.Federation.Workers.Retention},
# Walk a batch of stale remote actors every hour so cached public
# keys don't drift even when nobody's touching the actor.
{"@hourly", GitGud.Federation.Workers.RefreshRemoteActors},
# Prune `actions/cache` entries by age + per-repo size cap.
{"@hourly", GitGud.Workflows.Workers.CacheEviction},
# Requeue workflow_jobs stuck in `running` after a claim-then-
# can't-send (network blip, encoder bug, runner crash). Threshold
# is configurable via :stale_after_minutes; default 5 minutes.
{"* * * * *", GitGud.Workflows.Workers.StaleJobReaper}
]}
]
# Stale-job reaper threshold. Lower = faster recovery, higher = more
# tolerant of slow runners. Tune per deployment.
config :git_gud, GitGud.Workflows.Workers.StaleJobReaper,
stale_after_minutes: 5
# Federation retention. nil = keep forever. Tune per deployment.
config :git_gud, GitGud.Federation.Workers.Retention,
inbox_deliveries_days: nil,
outbound_deliveries_days: nil,
remote_activities_days: nil
# CI cache eviction. Either knob can be `nil` to disable that
# dimension; setting both gives "age-then-size" pruning.
# zot → Packages mirror. The shared secret guards the webhook
# endpoint at `POST /_webhooks/zot`. Configure zot's notification
# block with `Authorization: Bearer <same value>`. Leave as `nil` for
# loopback-only dev where the URL is the only thing keeping it private.
config :git_gud, GitGudWeb.PackagesWebhookController, shared_secret: nil
# OCI registry Bearer-token signing key. The private PEM is wired in
# `config/runtime.exs` from GITGUD_REGISTRY_PRIVATE_KEY. zot validates
# signatures against the matching public PEM at /etc/zot/registry-pub.pem;
# `kid` must agree on both sides or zot rejects with `invalid signature`.
config :git_gud, GitGud.Packages.Token,
issuer: "gitgud-registry",
audience: "container_registry",
key_id: "gitgud-registry-1",
ttl_seconds: 300,
private_key_pem: nil
# Bundled OCI registry host (zot). When set, every CI job receives it as
# the built-in variable `vars.CI_REGISTRY` so workflows can
# `docker login`/push without per-repo config. Wired from
# GITGUD_REGISTRY_HOST in runtime.exs; nil = not injected (workflows must
# supply their own registry).
config :git_gud, :registry_host, nil
config :git_gud, GitGud.Workflows.Workers.CacheEviction,
# Drop entries with `last_used_at` (or `archived_at` when never read)
# older than this many days.
max_age_days: 90,
# Per-repo total size cap in MB. After age pruning, anything past the
# cap (LRU on last_used_at) gets evicted.
per_repo_max_mb: 500,
# When true, log what would be evicted but don't touch the DB or
# storage. Handy when tuning the knobs against a populated DB.
dry_run: false
# Inbox rate limiting (per source host, fixed-minute window).
# Allowlisted instances get a higher cap (multiplier × base). Set
# `allowlist_multiplier: nil` to disable the boost.
config :git_gud, GitGud.Federation.RateLimiter,
inbox_per_minute: 60,
allowlist_multiplier: 5
# Register `application/protobuf` so the runner Twirp endpoints can
# `accept ["protobuf", "json"]`. After changing this, run
# `mix deps.clean --build mime` so the MIME library picks it up.
config :mime, :types, %{
"application/protobuf" => ["protobuf"]
}
# Secrets encryption. The default key is for dev/test; production
# MUST override in runtime.exs from an env var or KMS-fetched value.
# 32 raw bytes, base64-encoded.
config :git_gud, GitGud.Secrets,
primary_key_id: "default",
master_key: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
# Per-task JWT signing secret used by the runner pipeline. Override in
# runtime.exs in production.
config :git_gud, GitGud.Workflows.RunnerJwt,
secret: "dev-runner-jwt-secret-do-not-use-in-prod",
ttl_seconds: 86_400
# Embedded SSH daemon. Off by default; enabled in dev.exs. Set to true
# in runtime.exs for production after deciding on the port + host key
# location. Port 2222 avoids conflict with system OpenSSH on 22.
config :git_gud, GitGud.Ssh.Server,
enabled: false,
port: 2222,
host_key_dir: Path.expand("../priv/ssh", __DIR__),
# Hostname + port clients should connect to. Used by the UI to render
# `ssh://user@<host>:<port>/owner/repo.git`. `external_host` falls
# back to the endpoint URL's host; `external_port` falls back to
# `:port`. Both are split from the internal listener so a deployment
# can listen internally on 2222 while a front (Traefik, k8s LB,
# nginx) maps the public port (e.g. 22222 or 22).
external_host: nil,
external_port: nil
# Repository storage. Override per-environment in dev.exs/runtime.exs.
config :git_gud, GitGud.Repositories.Storage, base_path: "priv/repos"
# Object storage backend. The Disk impl is appropriate for local dev,
# single-node prod, and multi-node prod over a shared NFS mount.
# Swap to `GitGud.Storage.Garage` for S3-flavored object storage.
config :git_gud, GitGud.Storage, impl: GitGud.Storage.Disk
config :git_gud, GitGud.Storage.Disk, root: "priv/storage"
# Two-factor authentication policy.
# :required — when true, enforce TOTP for every account
# :grace_period_days — days after confirmed_at during which a user
# can still use the forge while choosing to
# enroll; afterward they're redirected to
# enrollment until they finish.
config :git_gud, GitGud.Accounts.TwoFactor,
required: false,
grace_period_days: 14
# In-memory cache, L1 + L2.
#
# L1 = per-node ETS. Tighter caps; this tier turns over quickly under
# typical browsing patterns.
config :git_gud, GitGud.Cache.L1,
gc_interval: :timer.hours(12),
max_size: 50_000,
allocated_memory: 200_000_000
# L2 = Replicated across cluster nodes. Larger caps; L2 is what
# survives across node hops, so it's the "real" working set.
config :git_gud, GitGud.Cache.L2,
primary: [
gc_interval: :timer.hours(24),
max_size: 100_000,
allocated_memory: 400_000_000
]
# Multilevel aggregator — reads cascade L1 → L2, writes fan out to both.
# Inclusive model promotes L2 hits back into L1 on the way back.
config :git_gud, GitGud.Cache,
model: :inclusive,
levels: [
{GitGud.Cache.L1, []},
{GitGud.Cache.L2, []}
]
# Garage (S3-compatible). Endpoint, region, and keys are all set in
# runtime.exs from env vars.
config :ex_aws,
json_codec: Jason,
access_key_id: [{:system, "GARAGE_ACCESS_KEY_ID"}, :instance_role],
secret_access_key: [{:system, "GARAGE_SECRET_ACCESS_KEY"}, :instance_role]
# Configure the endpoint
config :git_gud, GitGudWeb.Endpoint,
url: [host: "localhost"],
adapter: Bandit.PhoenixAdapter,
render_errors: [
formats: [html: GitGudWeb.ErrorHTML, json: GitGudWeb.ErrorJSON],
layout: false
],
pubsub_server: GitGud.PubSub,
live_view: [signing_salt: "FRxtZcCV"]
# Configure the mailer
#
# By default it uses the "Local" adapter which stores the emails
# locally. You can see the emails in your browser, at "/dev/mailbox".
#
# For production it's recommended to configure a different adapter
# at the `config/runtime.exs`.
config :git_gud, GitGud.Mailer, adapter: Swoosh.Adapters.Local
# Configure esbuild (the version is required)
config :esbuild,
version: "0.25.4",
git_gud: [
args:
~w(js/app.js --bundle --target=es2022 --outdir=../priv/static/assets/js --external:/fonts/* --external:/images/* --alias:@=.),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => [Path.expand("../deps", __DIR__), Mix.Project.build_path()]}
]
# Configure tailwind (the version is required)
config :tailwind,
version: "4.1.12",
git_gud: [
args: ~w(
--input=assets/css/app.css
--output=priv/static/assets/css/app.css
),
cd: Path.expand("..", __DIR__)
]
# Configure Elixir's Logger
config :logger, :default_formatter,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
# Sentry error tracking. The DSN is wired from SENTRY_DSN in
# runtime.exs; when unset (dev/test) Sentry no-ops. Source context lets
# stacktraces show the offending lines.
config :sentry,
# Req-backed client; the default hackney one gets an empty response
# body from our GlitchTip and reports false send failures.
client: GitGud.SentryClient,
environment_name: config_env(),
enable_source_code_context: true,
root_source_code_paths: [File.cwd!()]
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{config_env()}.exs"