~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
382 lines · 14.6 KB Raw History
# knotless-serve and its pages, and knotless-run if asked, as
# /etc/knotless/docker-compose.yml.
# deploy.sh copies it there, beside the .env it reads; edit it here.
#
# TLS is left to a reverse proxy in front: it joins the network named by
# KNOTLESS_PROXY_NETWORK and proxies the domain to knotless-serve:2590.
#
# deploy.sh moves serve and pages with rollout.sh, which starts a new
# container beside the old under the same names and stops the old once the
# new one's healthcheck passes: neither sets a container_name, so two can
# run at once.

name: knotless

# Each container's output, JSON lines for serve and the runner, kept in
# the host's journal (deploy/README.md, Logs), which outlives the
# container: a rollout removes the old one, and its lines stay. Each line
# names its project and service (COM_DOCKER_COMPOSE_PROJECT and _SERVICE)
# and its container (CONTAINER_TAG).
x-logging: &logging
  driver: journald
  options:
    labels: com.docker.compose.project,com.docker.compose.service
    tag: "{{.Name}}"

services:
  serve:
    image: knotless:current
    restart: always
    logging: *logging
    command:
      - knotless-serve
      - --listen
      - 0.0.0.0:2590
      - --public-url
      - https://${KNOTLESS_DOMAIN:?set KNOTLESS_DOMAIN in .env}
      - --index
      - /data/index.db
      # What feeds the index: Jetstream, a relay's firehose, or both.
      # KNOTLESS_JETSTREAM set empty turns Jetstream off; KNOTLESS_FIREHOSE
      # (a relay, wss://relay1.us-east.bsky.network) turns the firehose on.
      # KNOTLESS_BACKFILL (https://relay1.us-east.bsky.network) refills the
      # index from a relay at each start, for what a feed down missed.
      - --jetstream
      - ${KNOTLESS_JETSTREAM-wss://jetstream.us-east.bsky.network}
      - --firehose
      - ${KNOTLESS_FIREHOSE:-}
      - --backfill
      - ${KNOTLESS_BACKFILL:-}
      # When an upgrade rebuilds the index, and every ten minutes after,
      # it refills from the accounts this relay lists as holding knotless
      # records.
      - --relay
      - ${KNOTLESS_RELAY:-https://relay1.us-east.bsky.network}
      # Which accounts hold knotless records is asked of microcosm's
      # lightrail before the relay, and each account's repository is read
      # from its hubble mirror before the account's PDS. Either set empty
      # is not asked.
      - --directory
      - ${KNOTLESS_DIRECTORY-https://lightrail.microcosm.blue}
      - --mirror
      - ${KNOTLESS_MIRROR-https://hubble.microcosm.blue}
      - --web
      - http://pages:2591
      # The client's builds, as the repository's releases of its
      # `knotless` package, which the release workflow publishes.
      - --releases
      - knotless://chadtmiller.com/knotless
      # Repositories' sites, on a domain of their own (knotless.page), when
      # KNOTLESS_PAGES_DOMAIN names one; setup.sh's Caddy holds its
      # certificate. Empty answers none.
      - --pages-domain
      - ${KNOTLESS_PAGES_DOMAIN:-}
      # Apple's push service, for the iOS app's notifications: deploy.sh
      # sets the key's path once the key is on the host (deploy/README.md).
      # Empty, none is sent.
      - --apns-key
      - ${KNOTLESS_APNS_KEY:-}
      - --apns-key-id
      - ${KNOTLESS_APNS_KEY_ID:-}
      - --apns-team
      - ${KNOTLESS_APNS_TEAM:-}
      # /metrics and PUT /log, inside the container alone: read them
      # with docker exec (deploy/README.md, Logs).
      - --metrics
      - 127.0.0.1:9464
      # How it is used, for `knotless admin stats`: counts, never who.
      - --usage
      - /data/usage.db
      # The lines it logs, kept to read back across deploys, for
      # `knotless admin logs` and MCP's logs (deploy/README.md, Logs).
      - --logs
      - /data/logs.db
      # Where the bots' OAuth sessions are kept once they log in at
      # /oauth/bot: run and triage read them from there.
      - --bots
      - /data/bots
      # Code search: the Zoekt shards of every public repository's default
      # branch, written from its mirror as it is pushed.
      - --code-index
      - /data/code
      # The runner a new public repository names unless its owner names
      # others: the runner host's account (runner-host/README.md). Empty,
      # none.
      - --default-runner
      - ${KNOTLESS_DEFAULT_RUNNER_DID:-}
      # The oldest client version to ask for, and why: set when one harms
      # repositories, and clients older refuse to run until upgraded.
      # Empty, none.
      - --client-minimum
      - ${KNOTLESS_CLIENT_MINIMUM:-}
      - --client-minimum-reason
      - ${KNOTLESS_CLIENT_MINIMUM_REASON:-}
      # The oldest build of the iOS app to let run, and why: older builds
      # show only a way to update. Empty, any runs (ios/README.md).
      - --ios-minimum-build
      - ${KNOTLESS_IOS_MINIMUM_BUILD:-}
      - --ios-minimum-reason
      - ${KNOTLESS_IOS_MINIMUM_REASON:-}
    environment:
      # The gateway's OAuth client key and the sessions owners delegate
      # are in the config directory; mirrors and identities in the cache.
      KNOTLESS_CONFIG_DIR: /data/config
      KNOTLESS_CACHE_DIR: /data/cache
      # The accounts, by DID, that operate it: takedowns, reports, usage.
      KNOTLESS_ADMIN_DIDS: ${KNOTLESS_ADMIN_DIDS:-}
      # The accounts, by DID, that may log in at /oauth/bot: run's and
      # triage's.
      KNOTLESS_BOT_DIDS: ${KNOTLESS_BOT_DIDS:-}
      # What is logged, as RUST_LOG takes it: info, unless .env says.
      RUST_LOG: ${KNOTLESS_LOG:-info}
    volumes:
      # Owned by uid 1000, the node user the image runs as.
      - ${KNOTLESS_DATA:-/var/lib/knotless}:/data
    networks:
      default: {}
      # The name the reverse proxy reaches it by.
      proxy:
        aliases: [knotless-serve]
    depends_on:
      - pages
    # Ahead of CI for the host's cores: a run's containers have docker's
    # default share (1024), so while they keep every core busy the site
    # still gets four times theirs.
    cpu_shares: 4096
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 2g
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:2590/healthz').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))"]
      interval: 30s
      timeout: 5s
      retries: 3
      # Each second while it starts, so a rollout hands over as soon as it
      # listens rather than at the first 30s check.
      start_period: 120s
      start_interval: 1s
    # Stopped, it finishes the requests it has: a push or a clone can take
    # a while.
    stop_grace_period: 30s

  pages:
    image: knotless:current
    restart: always
    logging: *logging
    command: ["node", "/app/build"]
    environment:
      HOST: 0.0.0.0
      PORT: "2591"
      KNOTLESS_SERVE: http://serve:2590
      # knotless-serve hands on the host and scheme it was asked for.
      HOST_HEADER: x-forwarded-host
      PROTOCOL_HEADER: x-forwarded-proto
    # A file the build writes, answered without asking serve anything.
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:2591/_app/version.json').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s
      start_interval: 1s
    # Stopped, it finishes the requests it has, for up to adapter-node's
    # SHUTDOWN_TIMEOUT (30s).
    stop_grace_period: 35s
    cpu_shares: 4096
    deploy:
      resources:
        limits:
          # Two: at one, node's garbage collector and a render share a
          # core, and the page waits out the rest of the period throttled.
          cpus: "2"
          memory: 512m

  # knotless-run, only with COMPOSE_PROFILES=run, which deploy.sh sets
  # when KNOTLESS_RUN_REPOS names repositories to watch.
  run:
    image: knotless:current
    profiles: [run]
    restart: always
    logging: *logging
    # Root: what holds the docker socket is root on the host already, and
    # steps, run as root in their containers, leave files in the checkout
    # that only root can remove.
    user: root
    # Stopped, it drops each run still waiting its turn, for the next
    # start to run, and finishes only the runs whose steps have started,
    # a batch's and those at a going deploy's commit: most finish in a few
    # minutes, test in about 9, a release's build of the client in about
    # 17. A deploy does not wait for that: it starts the next runner beside
    # it (handover.sh), which lends the old one the jobs its runs hold, so
    # waiting costs nothing, and only a stop by hand waits. --stop-wait is
    # an hour, so that no run is cut off that its own limits let go on: a
    # step that hangs ends at --step-timeout (30 minutes). Past the hour it
    # cancels those still going itself, which a stop nothing comes to kill
    # needs. 65 minutes is that with room to spare; past it, docker kills
    # the runner, and the next ends what was cut off as an error and
    # removes its containers.
    stop_grace_period: 65m
    command:
      - sh
      - -c
      - >-
        exec knotless-run
        --jetstream ${KNOTLESS_JETSTREAM:-wss://jetstream.us-east.bsky.network}
        --executor docker
        --secrets /etc/knotless-run/run-secrets.json
        --state ${KNOTLESS_RUN_DATA:-/var/lib/knotless-run}/state.json
        --work ${KNOTLESS_RUN_DATA:-/var/lib/knotless-run}/work
        --caches ${KNOTLESS_RUN_DATA:-/var/lib/knotless-run}/caches
        --stop-wait 3600
        ${KNOTLESS_RUN_ARGS:-}
        ${KNOTLESS_RUN_REPOS:-}
    environment:
      # The appview beside it, where --pulls finds pulls.
      KNOTLESS_APPVIEW: http://serve:2590
      # The bots' sessions, which serve keeps (--bots).
      KNOTLESS_CONFIG_DIR: /bots
      KNOTLESS_CACHE_DIR: ${KNOTLESS_RUN_DATA:-/var/lib/knotless-run}/cache
      RUST_LOG: ${KNOTLESS_LOG:-info}
    env_file:
      # KNOTLESS_IDENTIFIER, the account the checks are written in, and
      # KNOTLESS_PASSWORD, an app password for it, used only until it logs
      # in at /oauth/bot: an app password reads no private repositories.
      - path: /etc/knotless/run.env
        required: false
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      # At the same path as on the host: a step's container mounts its
      # checkout by the path the host's daemon knows it by.
      - ${KNOTLESS_RUN_DATA:-/var/lib/knotless-run}:${KNOTLESS_RUN_DATA:-/var/lib/knotless-run}
      # Which secrets each repository's workflows may have, by
      # <handle>/<name>, in run-secrets.json, read again at each run. The
      # directory, not the file: an edit that writes a new file, as most
      # do, would leave a file's mount on the old one until a restart. The
      # docker socket above gives the runner the host already, so the
      # rest of /etc/knotless costs nothing more.
      - /etc/knotless:/etc/knotless-run:ro
      - ${KNOTLESS_DATA:-/var/lib/knotless}/bots:/bots
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: 512m

  # Triages issues with a decision model, writing labels as its own
  # account (knotless-triage). /etc/knotless/triage.env holds that
  # account's KNOTLESS_IDENTIFIER and KNOTLESS_PASSWORD, an app password
  # used until it logs in at /oauth/bot, and the model's endpoint and
  # token, KNOTLESS_DECIDE_URL and KNOTLESS_DECISION_TOKEN. It keeps
  # nothing but its session: what it has judged is in its records.
  triage:
    image: knotless:current
    profiles: [triage]
    restart: always
    logging: *logging
    command:
      - sh
      - -c
      - >-
        exec knotless-triage
        --decide "$$KNOTLESS_DECIDE_URL"
        --jetstream ${KNOTLESS_JETSTREAM:-wss://jetstream.us-east.bsky.network}
        ${KNOTLESS_TRIAGE_ARGS:-}
        ${KNOTLESS_TRIAGE_REPOS:-}
    environment:
      # The appview beside it, where it finds issues.
      KNOTLESS_APPVIEW: http://serve:2590
      KNOTLESS_CONFIG_DIR: /bots
      KNOTLESS_CACHE_DIR: /tmp/knotless-triage/cache
      RUST_LOG: ${KNOTLESS_LOG:-info}
    env_file:
      - path: /etc/knotless/triage.env
        required: false
    volumes:
      - ${KNOTLESS_DATA:-/var/lib/knotless}/bots:/bots
    deploy:
      resources:
        limits:
          cpus: "0.25"
          memory: 128m

  # Reviews the pulls that ask its account for a review, with Claude Code
  # (knotless-agent review, knotless-claude-review), writing each review
  # as that account's. /etc/knotless/review.env holds the account's
  # KNOTLESS_IDENTIFIER and KNOTLESS_PASSWORD, an app password, and
  # Claude's login: CLAUDE_CODE_OAUTH_TOKEN, from `claude setup-token`,
  # or ANTHROPIC_API_KEY. It keeps nothing but its session: what it has
  # reviewed is in its records. Private repositories it reads with the
  # OAuth session it gets at /oauth/bot, which KNOTLESS_BOT_DIDS lets it.
  review:
    image: knotless:current
    profiles: [review]
    restart: always
    logging: *logging
    command:
      - sh
      - -c
      - >-
        exec knotless-agent review
        --command knotless-claude-review
        --jetstream ${KNOTLESS_JETSTREAM:-wss://jetstream.us-east.bsky.network}
        ${KNOTLESS_REVIEW_ARGS:-}
        ${KNOTLESS_REVIEW_REPOS:-}
    environment:
      # The appview beside it, where it finds pulls; Claude reads them
      # from there too.
      KNOTLESS_APPVIEW: http://serve:2590
      KNOTLESS_CONFIG_DIR: /bots
      KNOTLESS_CACHE_DIR: /tmp/knotless-review/cache
      RUST_LOG: ${KNOTLESS_LOG:-info}
    env_file:
      - path: /etc/knotless/review.env
        required: false
    volumes:
      - ${KNOTLESS_DATA:-/var/lib/knotless}/bots:/bots
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: 1g

  # Keeps the pulls the merge queue turned away moving (knotless-agent
  # keep): one that conflicts only where both sides added lines it brings
  # up to date and queues again. /etc/knotless/keep.env holds its
  # account's KNOTLESS_IDENTIFIER and KNOTLESS_PASSWORD, an app password;
  # the account is a collaborator of each repository it keeps. It runs
  # no model and keeps nothing but its session.
  keep:
    image: knotless:current
    profiles: [keep]
    restart: always
    logging: *logging
    command:
      - sh
      - -c
      - >-
        exec knotless-agent keep
        --jetstream ${KNOTLESS_JETSTREAM:-wss://jetstream.us-east.bsky.network}
        ${KNOTLESS_KEEP_ARGS:-}
        ${KNOTLESS_KEEP_REPOS:-}
    environment:
      KNOTLESS_APPVIEW: http://serve:2590
      KNOTLESS_CONFIG_DIR: /bots
      KNOTLESS_CACHE_DIR: /tmp/knotless-keep/cache
      RUST_LOG: ${KNOTLESS_LOG:-info}
    env_file:
      - path: /etc/knotless/keep.env
        required: false
    volumes:
      - ${KNOTLESS_DATA:-/var/lib/knotless}/bots:/bots
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512m

networks:
  proxy:
    name: ${KNOTLESS_PROXY_NETWORK:-knotless}
    external: true