log:
  level: 0
  isJSON: true

rpcServer:
  address: :9091

services:
  project:
    projectsRootDir: /data/projects
    codexHomeDir: /home/node/.codex
    gitBinary: git
    maxEditableFileSize: 1048576
    defaultPageSize: 100
    maxPageSize: 500
    maxPathLength: 4096
    maxPathDepth: 64
    ignoredProjectDirectories:
      - lost+found
    deniedDirectories:
      - .git
      - .hg
      - .svn
      - node_modules
      - .venv
      - venv
      - vendor
      - dist
      - build
      - .next
      - .cache
      - __pycache__

  # One shared AWS Secrets Manager JSON secret, using datasource-proxy field names:
  # DEVOPS_API_TOKEN, DEVOPS_API_KEY (fallback), CLINIC_API_KEY,
  # JIRA_BASIC_AUTH_RAW (raw email:api-token, not Base64), GITHUB_PAT,
  # DASHSCOPE_API_KEY, AWS_BEARER_TOKEN_BEDROCK.
  # Prod and staging routes share their service credentials.
  # Store credential values in Secrets Manager, never in this configuration.
  secretStore:
    runtimes:
      dev-agent-codex-sidecar-secrets:
        source:
          type: awsSecretsManager
          awsSecretsManager:
            region: us-west-2
            secretId: dev-agent-codex-sidecar-secrets
            format: json
        refresh:
          interval: 5m
          maxStaleness: 1h
      # The shared ASM JSON also contains LARK_APP_SECRET and LARK_PROXY_KEY.
      # LARK_PROXY_KEY can be generated as 64 random hex characters; both sides
      # use those exact characters as HMAC key bytes (without decoding).
      lark-bot-token:
        source:
          type: larkTenantToken
          larkTenantToken:
            appId: cli_example
            brand: feishu
            appSecret:
              secret: dev-agent-codex-sidecar-secrets
              field: LARK_APP_SECRET
            requestTimeout: 10s
        refresh:
          refreshBeforeExpiry: 5m
      # Equivalent to O11Y_ENABLED=true: exchange the PAT and refresh auth0Token.
      o11y-session:
        source:
          type: o11yExchange
          o11yExchange:
            environment: prod
            endpoint: https://www.ds.us-west-2.aws.observability.tidbcloud.com/client/auth
            tokenTTL: 12h
            requestTimeout: 10s
            bootstrap:
              secret: dev-agent-codex-sidecar-secrets
              field: GITHUB_PAT
            cloudProvider: aws
        refresh:
          refreshBeforeExpiry: 1h
          maxStaleness: 0s

  # Covers datasource-proxy's upstreams and route-specific query/inference POSTs,
  # plus its default agent-mode Alertstore callback. Remove both DevOps PATCH
  # entries for readonly mode. There is no full-mode switch: add each required
  # method/path explicitly; CONNECT and TRACE are unsupported.
  #
  # Migration differences that configuration cannot remove:
  # - Named Unix/TCP listeners isolate route sets; /healthz is built in.
  # - /o11y/credentials (O11Y_EXPOSE_CREDENTIALS), direct data export and S3
  #   credential access are unsupported; o11yExchange retains only auth0Token.
  # - Upstreams require HTTPS, including o11y GS (datasource-proxy used HTTP).
  # - Host parameters use explicit allowedValues, not arbitrary matching labels.
  # - Headers are allowlisted; add required client headers per route below.
  #   Response headers have a fixed allowlist that cannot be configured here.
  # - Streaming uses caller cancellation/deadlines, without the old 30s/10m
  #   per-route timeout settings. SSE and Bedrock event streams are forwarded.
  # - Secrets refresh from AWS Secrets Manager; local secrets.json is not read.
  #
  # Routes on each listener use the longest matching segment-boundary prefix.
  # Nested prefixes are allowed; duplicate prefixes are rejected. A child route
  # owns its policies and credentials, with no fallback to its parent on failure.
  # Policies see the upstream path AFTER the route prefix and host-parameter
  # segments are stripped. Each route needs operation-policy BEFORE credential-auth.
  # Example: curl --unix-socket /run/agent-sidecar/api-proxy.sock \
  #   http://localhost/devops/prod/api/v1/...
  apiProxy:
    listeners:
      internal:
        network: unix
        address: /run/agent-sidecar/api-proxy.sock
        mode: "0660"
      lark:
        network: tcp
        address: 127.0.0.1:16384
    routes:
      lark-bot:
        listenerId: lark
        prefix: /
        upstream:
          baseUrl: https://open.feishu.cn
        middlewares:
          - type: lark-auth-proxy
            config:
              keyFrom:
                secret: dev-agent-codex-sidecar-secrets
                field: LARK_PROXY_KEY
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathExact: /open-apis/bot/v3/info
          - type: credential-auth
            config:
              candidates:
                - type: bearer
                  valueFrom:
                    secret: lark-bot-token
                    field: tenantAccessToken
      # Agent-mode callback enabled; candidates try API token first, then API key.
      devops-prod:
        listenerId: internal
        prefix: /devops/prod
        upstream:
          baseUrl: https://prod.devops.pingcap.com
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
                - method: PATCH
                  pathTemplate: /api/v1/alertstore/alerts/{alertId}/agent-runs/{runId}
          - type: credential-auth
            config:
              candidates:
                - type: header
                  headerName: X-API-TOKEN
                  valueFrom:
                    secret: dev-agent-codex-sidecar-secrets
                    field: DEVOPS_API_TOKEN
                - type: header
                  headerName: X-API-KEY
                  valueFrom:
                    secret: dev-agent-codex-sidecar-secrets
                    field: DEVOPS_API_KEY
      # /o11y/ds/us-west-2/aws/internal/metrics/... consumes region, then vendor.
      o11y-ds:
        listenerId: internal
        prefix: /o11y/ds
        upstream:
          scheme: https
          hostTemplate: www.ds.{region}.{vendor}.observability.tidbcloud.com
          hostParameters:
            # Example deployment regions; extend to your actual cluster regions.
            region:
              source: pathSegment
              allowedValues: [us-west-2, us-east-1, ap-southeast-1, us-central1]
            vendor:
              source: pathSegment
              allowedValues: [aws, gcp, alicloud]
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
          - type: credential-auth
            config:
              candidates:
                - type: bearer
                  valueFrom:
                    secret: o11y-session
                    field: auth0Token
      o11y-gs:
        listenerId: internal
        prefix: /o11y/gs
        upstream:
          baseUrl: https://www.gs.us-west-2.aws.observability.tidbcloud.com
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
          - type: credential-auth
            config:
              candidates:
                - type: bearer
                  valueFrom:
                    secret: o11y-session
                    field: auth0Token
      clinic-prod:
        listenerId: internal
        prefix: /clinic/prod
        upstream:
          baseUrl: https://clinic.pingcap.com
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
                - method: POST
                  pathExact: /data-proxy/query
                - method: POST
                  pathExact: /data-proxy/batch-query
                - method: POST
                  pathTemplate: /data-proxy/api/v1/orgs/{orgId}/clusters/{clusterId}/sql-queries
                - method: POST
                  pathTemplate: /data-proxy/api/v1/orgs/{orgId}/clusters/{clusterId}/sql-query-batches
                - method: POST
                  pathTemplate: /data-proxy/api/v1/orgs/{orgId}/clusters/{clusterId}/metrics/query
                - method: POST
                  pathTemplate: /data-proxy/api/v1/orgs/{orgId}/clusters/{clusterId}/metrics/query-range
          - type: credential-auth
            config:
              candidates:
                - type: bearer
                  valueFrom:
                    secret: dev-agent-codex-sidecar-secrets
                    field: CLINIC_API_KEY
      jira:
        listenerId: internal
        prefix: /jira
        upstream:
          baseUrl: https://tidb.atlassian.net
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
                - method: POST
                  pathExact: /rest/api/3/search/jql
          - type: credential-auth
            config:
              candidates:
                - type: basic
                  valueFrom:
                    secret: dev-agent-codex-sidecar-secrets
                    field: JIRA_BASIC_AUTH_RAW
      # Git clone/fetch, including protocol v2; git-receive-pack (push) is not allowed.
      github:
        listenerId: internal
        prefix: /github
        upstream:
          baseUrl: https://github.com
        allowedRequestHeaders:
          - Git-Protocol
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
                - method: POST
                  pathTemplate: /{repository...}/git-upload-pack
          - type: credential-auth
            config:
              candidates:
                - type: githubPATBasic
                  valueFrom:
                    secret: dev-agent-codex-sidecar-secrets
                    field: GITHUB_PAT
      dashscope:
        listenerId: internal
        prefix: /dashscope
        upstream:
          baseUrl: https://dashscope-intl.aliyuncs.com
        allowedRequestHeaders:
          - X-DashScope-SSE
          - X-DashScope-WorkSpace
          - Anthropic-Version
          - Anthropic-Beta
        middlewares:
          - type: operation-policy
            config:
              allow:
                - method: GET
                  pathPrefix: /
                - method: HEAD
                  pathPrefix: /
                - method: POST
                  pathExact: /compatible-mode/v1/chat/completions
                - method: POST
                  pathExact: /compatible-mode/v1/responses
                - method: POST
                  pathExact: /compatible-mode/v1/embeddings
                - method: POST
                  pathExact: /api/v1/services/aigc/text-generation/generation
                - method: POST
                  pathExact: /apps/anthropic/v1/messages
                - method: POST
                  pathExact: /apps/anthropic/v1/messages/count_tokens
          - type: credential-auth
            config:
              candidates:
                - type: bearer
                  valueFrom:
                    secret: dev-agent-codex-sidecar-secrets
                    field: DASHSCOPE_API_KEY
