version: 1
kind: HwlabWebProbeSentinelProfiles
metadata:
  id: hwlab-web-probe-sentinel-profiles
  owner: UniDesk
  specRef: PJ2026-01060508
  composition:
    mode: yaml-anchors-and-merge
    intent: node overlays inherit common web-probe sentinel baselines and render node/lane identity from variables.
baselines:
  sentinel:
    enabled: true
    mode: web-probe-observe-wrapper
  runtime:
    common:
      namespace: "hwlab-${lane}"
      listenHost: 0.0.0.0
      servicePort: 8080
      pvcStorage: 10Gi
      replicas: 1
      healthPath: /api/health
      metricsPath: /metrics
      scheduler:
        intervalMs: 600000
        heartbeatStaleSeconds: 900
        maxConcurrentRuns: 1
        freshnessWarningMultiple: 2
      observability:
        otel:
          enabled: true
          serviceName: "hwlab-web-probe-sentinel-${nodeLower}"
          tracesEndpoint: http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces
          sampler: parentbased_traceidratio
          samplerArg: "1"
      kubernetesApi:
        &kubernetesApi
        endpoint:
          host: 172.16.0.5
          port: 6443
        egress:
          enabled: true
          rules:
            - cidr: 10.43.0.1/32
              port: 443
            - cidr: 172.16.0.5/32
              port: 6443
    scheduler15m:
      intervalMs: 900000
      heartbeatStaleSeconds: 900
      maxConcurrentRuns: 1
    sqlite:
      busyTimeoutMs: 2000
  cicd:
    source:
      repository: pikasTech/unidesk
      branch: master
      gitSshUrl: ssh://git@ssh.github.com:443/pikasTech/unidesk.git
      gitMirrorReadUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/unidesk.git
      buildContext: "."
      entrypoint: scripts/web-probe-sentinel-service.ts
      checkoutPaths:
        - scripts
        - config
        - config.json
        - src
        - package.json
        - bun.lock
        - bun.lockb
    sourceAuthority:
      mode: gitMirrorSnapshot
      resolver: k8s-git-mirror
      allowHostGit: false
      allowGithubDirectInPipeline: false
    sourceSnapshot:
      stageRefPrefix: "refs/unidesk/snapshots/web-probe-sentinel/{branch}"
      missingObjectPolicy: fail-fast
      refreshPolicy: sync-before-snapshot
      cacheRef: source.gitMirrorReadUrl
    builder:
      namespace: devops-infra
      sourceMode: sparse-git-checkout
      gitSshSecretName: git-mirror-github-ssh
      activeDeadlineSeconds: 900
      ttlSecondsAfterFinished: 3600
    monitorWeb:
      frontendStack: vue3-vendored-browser-build
      runtimeMode: runner-served-bridge
      assetRoot: scripts/assets/web-probe-sentinel-monitor-web
      envReuse:
        mode: k8s-buildkit-and-ci-node-deps
        nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
      imageBuild:
        packageMode: copy-only-containerfile
        networkMode: host
        proxySource: node.networkProfile.imageBuildProxy
        contextIgnore: generated
        verifyPhase: pre-image-build
        buildkitState:
          mode: hostPath
          path: "/var/lib/unidesk/web-probe-sentinel/buildkit-${nodeLower}"
          type: DirectoryOrCreate
      gitMirror:
        source: source.gitMirrorReadUrl
        preSync: required
        postFlush: required
      ciBudget:
        maxSeconds: 120
    maintenance:
      startCommand: sentinel maintenance start
      stopCommand: sentinel maintenance stop
    confirmWait:
      maxSeconds: 120
    publishCurrent:
      endToEndBudget:
        maxSeconds: 120
      stageBudgets:
        sourceSyncSeconds: 20
        sourceFetchSeconds: 20
        monitorWebVerifySeconds: 15
        imageBuildSeconds: 45
        gitopsSeconds: 15
        argoRuntimeSeconds: 30
        dashboardVerifySeconds: 30
      dashboard:
        enabled: true
        required: true
        viewport: 1440x900
        timeoutMs: 30000
        waitTimeoutMs: 60000
        commandTimeoutSeconds: 90
        fullPage: false
  publicExposure:
    common:
      enabled: true
      mode: pk01-caddy-frp-path
      hostname: monitor.pikapython.com
      expectedA: 82.156.23.220
    frpc:
      image: 127.0.0.1:5000/hwlab/frpc:v0.68.1
      serverAddr: 82.156.23.220
      serverPort: 22000
      tokenSourceRef: platform-infra/pk01-frp.env
      tokenSourceKey: FRP_TOKEN
      secretKey: frpc.toml
      tokenKey: token
    caddy:
      route: PK01
      configPath: /etc/caddy/Caddyfile
      serviceName: caddy
      email: ops@pikapython.com
      tls: auto
      responseHeaderTimeoutSeconds: 600
  secrets:
    jd01BootstrapSource:
      purpose: bootstrap-admin
      sourceRef: /root/.unidesk/.env/unified-admin-password.txt
      sourceKey: contents
      sourceLine: 1
    dsflashPromptSource:
      purpose: prompt-set
      sourceRef: hwlab/web-probe-sentinel-dsflash-go.env
      sourceKey: DSFLASH_GO_TOOL_CALL_10X_PROMPTS_JSON
    frpTokenSource:
      purpose: frp-token
      sourceRef: platform-infra/pk01-frp.env
      sourceKey: FRP_TOKEN
nodes:
  JD01:
    target:
      extends: templates.webProbeSentinelTarget
    cicdCommon:
      extends: templates.webProbeSentinelCicdCommon
    sentinels:
      jd01-web-probe-sentinel:
        sentinel:
          extends: templates.webProbeSentinelSentinel
        runtime:
          extends: templates.webProbeSentinelRuntime
          kubernetesApi:
            *kubernetesApi
        publicExposure:
          extends: templates.webProbeSentinelPublicExposure
          frpc:
            httpProxy:
              remotePort: 22098
        cicd:
          extends: templates.webProbeSentinelCicd
        secrets:
          sources:
            - sourceLine: 1
              sourceKey: contents
              sourceRef: /root/.unidesk/.env/unified-admin-password.txt
              purpose: bootstrap-admin
            - sourceKey: DSFLASH_GO_TOOL_CALL_10X_PROMPTS_JSON
              sourceRef: hwlab/web-probe-sentinel-dsflash-go.env
              purpose: prompt-set
            - purpose: account-a
              sourceRef: /root/.unidesk/.env/unified-admin-password.txt
              sourceKey: contents
              sourceLine: 1
              format: web-account-json
              usernameSourceRef: /root/.unidesk/.env/HWLAB_admin.txt
              usernameSourceLine: 1
            - purpose: account-b
              sourceRef: "hwlab/${nodeLower}-${lane}-preset-users.env"
              sourceKey: "${NODE}_SECOND_USER_PASSWORD"
              format: web-account-json
              username: "${nodeLower}-sentinel@hwlab.local"
            - sourceKey: FRP_TOKEN
              sourceRef: platform-infra/pk01-frp.env
              purpose: frp-token
          runtimeSecrets:
            - name: "hwlab-web-probe-sentinel-${nodeLower}-bootstrap"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: bootstrap-admin
                  targetKey: bootstrap-admin-password
            - name: "hwlab-web-probe-sentinel-${nodeLower}-prompt-set"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: prompt-set
                  targetKey: prompts.json
            - name: "hwlab-web-probe-sentinel-${nodeLower}-accounts"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: account-a
                  targetKey: account-a.json
                - sourcePurpose: account-b
                  targetKey: account-b.json
            - name: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: frp-token
                  targetKey: token
    variables:
      LANE: v03
      controlPlaneIndex: "1"
      argoProjectName: hwlab-jd01
      NODE: JD01
      sourceGitSshUrl: ssh://git@ssh.github.com:443/pikasTech/unidesk.git
      sourceSnapshotPrefix: refs/unidesk/snapshots/gitea-actions/unidesk-master
  NC01:
    target:
      extends: templates.webProbeSentinelTarget
    cicdCommon:
      extends: templates.webProbeSentinelCicdCommon
    sentinels:
      nc01-web-probe-sentinel:
        sentinel:
          extends: templates.webProbeSentinelSentinel
        runtime:
          extends: templates.webProbeSentinelRuntime
          kubernetesApi:
            endpoint:
              host: 10.43.0.1
              port: 443
            egress:
              enabled: true
              rules:
                - cidr: 10.43.0.1/32
                  port: 443
        publicExposure:
          extends: templates.webProbeSentinelPublicExposure
          frpc:
            httpProxy:
              remotePort: 22084
        cicd:
          extends: templates.webProbeSentinelCicd
        secrets:
          sources:
            - purpose: bootstrap-admin
              sourceRef: /root/.unidesk/.env/unified-admin-password.txt
              sourceKey: contents
              sourceLine: 1
            - sourceKey: DSFLASH_GO_TOOL_CALL_10X_PROMPTS_JSON
              sourceRef: hwlab/web-probe-sentinel-dsflash-go.env
              purpose: prompt-set
            - purpose: account-a
              sourceRef: /root/.unidesk/.env/unified-admin-password.txt
              sourceKey: contents
              sourceLine: 1
              format: web-account-json
              username: admin
            - purpose: account-b
              sourceRef: "hwlab/${nodeLower}-${lane}-preset-users.env"
              sourceKey: "${NODE}_SECOND_USER_PASSWORD"
              format: web-account-json
              username: "${nodeLower}-sentinel@hwlab.local"
            - sourceKey: FRP_TOKEN
              sourceRef: platform-infra/pk01-frp.env
              purpose: frp-token
          runtimeSecrets:
            - name: "hwlab-web-probe-sentinel-${nodeLower}-bootstrap"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: bootstrap-admin
                  targetKey: bootstrap-admin-password
            - name: "hwlab-web-probe-sentinel-${nodeLower}-prompt-set"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: prompt-set
                  targetKey: prompts.json
            - name: "hwlab-web-probe-sentinel-${nodeLower}-accounts"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: account-a
                  targetKey: account-a.json
                - sourcePurpose: account-b
                  targetKey: account-b.json
            - name: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
              namespace: "hwlab-${lane}"
              data:
                - sourcePurpose: frp-token
                  targetKey: token
    variables:
      LANE: v03
      controlPlaneIndex: "2"
      argoProjectName: hwlab-v03
      NODE: NC01
      sourceGitSshUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
      sourceSnapshotPrefix: refs/unidesk/snapshots/gitea-actions/unidesk-master-nc01
templates:
  webProbeSentinelTarget:
    node: "${NODE}"
    lane: "${LANE}"
    publicOriginRef: "config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.public.webUrl"
  webProbeSentinelCicdCommon:
    controlPlaneConfigRef: "config/hwlab-node-control-plane.yaml#targets[${controlPlaneIndex}]"
    source:
      checkoutPaths:
        - scripts
        - config
        - config.json
        - src
        - package.json
        - bun.lock
        - bun.lockb
      entrypoint: scripts/web-probe-sentinel-service.ts
      buildContext: "."
      gitMirrorReadUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-unidesk.git
      gitSshUrl: "${sourceGitSshUrl}"
      branch: master
      repository: pikasTech/unidesk
    sourceAuthority:
      allowGithubDirectInPipeline: false
      allowHostGit: false
      resolver: gitea-mirror
      mode: giteaSnapshot
    sourceSnapshot:
      cacheRef: source.gitMirrorReadUrl
      refreshPolicy: gitea-controlled-snapshot
      missingObjectPolicy: fail-fast
      stageRefPrefix: "${sourceSnapshotPrefix}"
    argo:
      namespace: argocd
      projectName: "${argoProjectName}"
      repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
      targetRevision: v0.3-gitops
    maintenance:
      stopCommand: sentinel maintenance stop
      startCommand: sentinel maintenance start
    monitorWeb:
      ciBudget:
        maxSeconds: 120
      gitMirror:
        source: source.gitMirrorReadUrl
        preSync: not-required
        postFlush: not-required
      imageBuild:
        packageMode: copy-only-containerfile
        networkMode: host
        proxySource: node.networkProfile.imageBuildProxy
        contextIgnore: generated
        verifyPhase: pre-image-build
        buildkitState:
          mode: hostPath
          path: "/var/lib/unidesk/web-probe-sentinel/buildkit-${nodeLower}"
          type: DirectoryOrCreate
      envReuse:
        mode: k8s-buildkit-and-ci-node-deps
        nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
      assetRoot: scripts/assets/web-probe-sentinel-monitor-web
      runtimeMode: runner-served-bridge
      frontendStack: vue3-vendored-browser-build
    confirmWait:
      maxSeconds: 120
    publishCurrent:
      dashboard:
        enabled: true
        required: true
        viewport: 1440x900
        timeoutMs: 30000
        waitTimeoutMs: 60000
        commandTimeoutSeconds: 90
        fullPage: false
      stageBudgets:
        sourceSyncSeconds: 20
        sourceFetchSeconds: 20
        monitorWebVerifySeconds: 15
        imageBuildSeconds: 45
        gitopsSeconds: 15
        argoRuntimeSeconds: 30
        dashboardVerifySeconds: 30
      endToEndBudget:
        maxSeconds: 120
  webProbeSentinelSentinel:
    mode: web-probe-observe-wrapper
    enabled: true
    id: "${nodeLower}-web-probe-sentinel"
    configRefs:
      runtime: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime"
      scenarios: "config/hwlab-web-probe-sentinel/scenarios.multi-sentinel.yaml#sentinel.scenarios"
      promptSet: "config/hwlab-web-probe-sentinel/prompt-set.dsflash-go.yaml#sentinel.promptSet"
      reportViews: "config/hwlab-web-probe-sentinel/report-views.multi-sentinel.yaml#sentinel.reportViews"
      publicExposure: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.publicExposure"
      cicd: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.cicd"
      secrets: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.secrets"
  webProbeSentinelRuntime:
    kubernetesApi:
      endpoint:
        host: 172.16.0.5
        port: 6443
      egress:
        enabled: true
        rules:
          - cidr: 10.43.0.1/32
            port: 443
          - cidr: 172.16.0.5/32
            port: 6443
    observability:
      otel:
        enabled: true
        serviceName: "hwlab-web-probe-sentinel-${nodeLower}"
        tracesEndpoint: http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces
        sampler: parentbased_traceidratio
        samplerArg: "1"
    scheduler:
      freshnessWarningMultiple: 2
      maxConcurrentRuns: 1
      heartbeatStaleSeconds: 900
      intervalMs: 600000
    metricsPath: /metrics
    healthPath: /api/health
    replicas: 1
    pvcStorage: 10Gi
    servicePort: 8080
    listenHost: 0.0.0.0
    namespace: "hwlab-${lane}"
    target:
      extends: templates.webProbeSentinelTarget
      observeWrapperRef: "config/hwlab-node-lanes.yaml#lanes.${LANE}.targets.${NODE}.observability.webProbe.sentinels[0]"
    serviceAccountName: "hwlab-web-probe-sentinel-${nodeLower}"
    deploymentName: "hwlab-web-probe-sentinel-${nodeLower}"
    serviceName: "hwlab-web-probe-sentinel-${nodeLower}"
    pvcName: "hwlab-web-probe-sentinel-${nodeLower}-state"
    stateRoot: "/var/lib/web-probe-sentinel-${nodeLower}"
    artifactSources:
      - ownerPvc: "hwlab-web-probe-sentinel-${nodeLower}-state"
        mountPath: "/var/lib/web-probe-sentinel-${nodeLower}"
      - ownerPvc: "legacy-web-observe-workspace-${nodeLower}"
        mountPath: "/root/hwlab-v03"
        hostPath: "/root/hwlab-v03"
    imageRef: "127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}:source-commit"
    sqlite:
      busyTimeoutMs: 2000
      path: "/var/lib/web-probe-sentinel-${nodeLower}/index.sqlite"
  webProbeSentinelPublicExposure:
    expectedA: 82.156.23.220
    hostname: monitor.pikapython.com
    mode: pk01-caddy-frp-path
    enabled: true
    publicBaseUrl: "https://monitor.pikapython.com/sentinels/${nodeLower}-web-probe-sentinel"
    routePrefix: "/sentinels/${nodeLower}-web-probe-sentinel"
    frpc:
      tokenKey: token
      secretKey: frpc.toml
      tokenSourceKey: FRP_TOKEN
      tokenSourceRef: platform-infra/pk01-frp.env
      serverPort: 22000
      serverAddr: 82.156.23.220
      image: 127.0.0.1:5000/hwlab/frpc:v0.68.1
      deploymentName: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
      secretName: "hwlab-web-probe-sentinel-${nodeLower}-frpc"
      httpProxy:
        name: "hwlab-${nodeLower}-${lane}-web-probe-sentinel"
        remotePort: 0
        localIP: "hwlab-web-probe-sentinel-${nodeLower}.hwlab-${lane}.svc.cluster.local"
        localPort: 8080
    caddy:
      responseHeaderTimeoutSeconds: 600
      tls: auto
      email: ops@pikapython.com
      serviceName: caddy
      configPath: /etc/caddy/Caddyfile
      route: PK01
      managedBlockOwner: "hwlab-web-probe-sentinel-${nodeLower}-${lane}"
  webProbeSentinelCicd:
    extends: templates.webProbeSentinelCicdCommon
    builder:
      ttlSecondsAfterFinished: 3600
      activeDeadlineSeconds: 900
      gitSshSecretName: git-mirror-github-ssh
      sourceMode: sparse-git-checkout
      namespace: devops-infra
      jobPrefix: "web-probe-sentinel-${nodeLower}-publish"
    gitopsPath: "deploy/gitops/node/${nodeLower}/web-probe-sentinel"
    argo:
      applicationName: "hwlab-web-probe-sentinel-${nodeLower}"
    image:
      repository: "127.0.0.1:5000/hwlab/web-probe-sentinel-${nodeLower}"
      tagSource: source-commit
      baseImageRef: "config/hwlab-node-control-plane.yaml#targets[${controlPlaneIndex}].tekton.toolsImage.output"
      envRecipeRef: "config/hwlab-web-probe-sentinel/profiles.yaml#nodes.${NODE}.sentinels.${nodeLower}-web-probe-sentinel.runtime"
    targetValidation:
      scenarioId: workbench-dsflash-go-hwpod-two-turn-freeze-repro
      maxSeconds: 600
      serviceUnavailablePolicy: structured-failure
      cadenceScheduler:
        enabled: true
        reason: k8s-native-periodic-quick-verify
        concurrencyPolicy: Forbid
        startingDeadlineSeconds: 600
        successfulJobsHistoryLimit: 3
        failedJobsHistoryLimit: 5
        activeDeadlineSlackSeconds: 60
        ttlSecondsAfterFinished: 86400
        backoffLimit: 0
