# 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 :tarakan, secure_cookies: true # Contact published in /.well-known/security.txt; overridden in prod runtime. config :tarakan, security_contact: "security@example.com" config :tarakan, :scopes, account: [ default: true, module: Tarakan.Accounts.Scope, assign_key: :current_scope, access_path: [:account, :id], schema_key: :account_id, schema_type: :id, schema_table: :accounts, test_data_fixture: Tarakan.AccountsFixtures, test_setup_helper: :register_and_log_in_account ] config :tarakan, ecto_repos: [Tarakan.Repo], generators: [timestamp_type: :utc_datetime], github_client: Tarakan.GitHub.HTTPClient, github_bulk_client: Tarakan.GitHub.GraphQLClient, github_oauth_client: Tarakan.GitHub.OAuth.HTTPClient, gitlab_oauth_client: Tarakan.GitLab.OAuth.HTTPClient, # Finding-kind Request complete requires a Review Format document (Findings path). # Legacy prose remains for write_fix / verify_findings and for tests that opt into dual mode. request_completion_mode: :document_required, stripe_api: Tarakan.Billing.Stripe.API, stripe_secret_key: nil, stripe_webhook_secret: nil, # Placeholder plan prices; real ids come from runtime env in prod. stripe_price_enterprise: "price_enterprise_placeholder", # Platform share of bounties, snapshotted onto each bounty at creation. market_take_rate: 0.10 config :tarakan, :github, api_version: "2026-03-10", client_id: nil, client_secret: nil, api_token: nil config :tarakan, Tarakan.RepositoryCode, global_upstream_limit: 240, repository_upstream_limit: 60, upstream_window_seconds: 60, identity_cache_ttl_ms: 3_000, identity_revalidation_ttl_ms: 86_400_000, head_cache_ttl_ms: 12_000, immutable_cache_ttl_ms: 86_400_000 # Public aggregate queries (posture, badges, model analytics, the suppression # corpus). Short enough that the record still reads as live; long enough that # an unauthenticated endpoint cannot replay the query on demand. config :tarakan, Tarakan.AnalyticsCache, ttl_ms: 60_000 config :tarakan, Tarakan.RepositoryMirror, enabled: true, # Production code browse uses git mirrors only (no GitHub REST for objects). rest_fallback: false, root: "priv/mirrors" config :tarakan, Tarakan.HostedRepositories, root: "priv/hosted", max_push_bytes: 262_144_000, quota_bytes: 1_073_741_824 config :tarakan, Tarakan.GitSSH, enabled: false, port: 2222, host_key_dir: "priv/ssh" # Cap concurrent git upload-pack/receive-pack processes (HTTP RPC + SSH). config :tarakan, Tarakan.Git.Concurrency, max_concurrent: 32 config :tarakan, Oban, engine: Oban.Engines.Basic, repo: Tarakan.Repo, # infestations: 1 keeps DB pool pressure low (compose POOL_SIZE default is 5). queues: [sync: 5, mirror: 3, infestations: 1, credits: 1, market: 1, billing: 1], plugins: [ {Oban.Plugins.Pruner, max_age: 7 * 24 * 60 * 60}, {Oban.Plugins.Cron, crontab: [ {"0 3 * * *", Tarakan.Sync.RepositorySweep}, {"30 3 * * *", Tarakan.Infestations.Reconcile}, # No-op until :swarm_sweep names an actor; runs after the reconcile so # it sees the rollups that pass refreshed. {"45 3 * * *", Tarakan.Infestations.SwarmSweep}, {"0 4 * * 0", Tarakan.Sync.HostedRepositoryGC}, {"0 * * * *", Tarakan.Infestations.RecomputeWindows}, {"15 * * * *", Tarakan.Market.SweepWorker}, {"0 9 * * *", Tarakan.Billing.AlertWorker} ]} ] # The nightly swarm sweep fans check jobs onto other contributors' agents, so # it stays inert until an operator names the account it opens them as. config :tarakan, :swarm_sweep, actor_handle: nil, patterns_per_run: 25, jobs_per_run: 40, max_jobs_per_pattern: 8, min_repos: 3, days: 90 # Infestation rollups: async projection + dual-read. Tests set sync_refresh: true. config :tarakan, :infestations, read_from_rollup: true, refresh_async: true, sync_refresh: false config :tarakan, :gitlab, base_url: "https://gitlab.com", client_id: nil, client_secret: nil # Configure the endpoint config :tarakan, TarakanWeb.Endpoint, url: [host: "localhost"], adapter: Bandit.PhoenixAdapter, render_errors: [ formats: [html: TarakanWeb.ErrorHTML, json: TarakanWeb.ErrorJSON], layout: false ], pubsub_server: Tarakan.PubSub, live_view: [signing_salt: "Ht50NGTc"] # Configure LiveView config :phoenix_live_view, # the attribute set on all root tags. Used for Phoenix.LiveView.ColocatedCSS. root_tag_attribute: "phx-r" # 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 :tarakan, Tarakan.Mailer, adapter: Swoosh.Adapters.Local # Configure esbuild (the version is required) config :esbuild, version: "0.25.4", tarakan: [ args: ~w(js/app.js --bundle --target=es2022 --format=esm --splitting --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()]} ], # Separate profile because this one must ship as a classic script: it runs # blocking in before first paint, and a module is always deferred. theme: [ args: ~w(js/theme.js --bundle --target=es2017 --format=iife --outdir=../priv/static/assets/js), 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.3.0", tarakan: [ args: ~w( --input=assets/css/app.css --output=priv/static/assets/css/app.css ), cd: Path.expand("..", __DIR__), env: %{"NODE_PATH" => [Path.expand("../deps", __DIR__), Mix.Project.build_path()]} ] # 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, filter_parameters: [ "authorization", "code", "credential", "description", "document", "evidence", "findings", "findings_json", "notes", "password", "reason", "secret", "summary", "token" ] # 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"