Commit Graph

62 Commits

Author SHA1 Message Date
Oliver Kieselbach fd2b5c2da2 fix(ci): drop invalid SWA input, gate deploy on token via env guard
Azure/static-web-apps-deploy@v1 has no `skip_deploy_on_missing_secrets`
input (confirmed against its action.yml), so passing it did nothing but
emit an "Unexpected input(s)" warning — the intended "skip green on
Dependabot PRs" behaviour was never actually in effect.

Replace it with the documented mechanism: surface the deploy token as a
job-level env var and gate the deploy/close step with `if: env.DEPLOY_TOKEN
!= ''` (secrets cannot be referenced directly in an `if:`). On Dependabot
PRs the token is absent, so the step skips and the job stays green; real
deploys on main and internal PRs still run. Applied to both jobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 16:38:58 +02:00
Oliver Kieselbach a5d12ba1d0 chore(ci): unify Node toolchain on 22 across CI + engines pins
Node 20 reached EOL (2026-04-30); Node 22 is the current maintenance LTS
and already the runtime the MCP container ships (node:22-slim). Straighten
the version skew so per-component CI major == deploy runtime == engines floor:

- ci.yml: web, mcp and guardrails setup-node 20 -> 22
- combine-rules.yml: setup-node 20 -> 22
- mcp package.json: add engines.node ">=22" (matches Dockerfile)
- web package.json: add engines.node ">=22" (first explicit pin; SWA/Oryx
  no longer picks a Node version implicitly)

engine-strict stays off (no .npmrc), so the pins are advisory and cannot
hard-break local installs on other Node versions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 16:15:54 +02:00
Oliver Kieselbach 829ec4c969 fix(release): stamp Cache-Control no-cache on every blob upload
The first release after the download-alias migration exposed Front Door
caching as a correctness hazard: the alias kept serving the previous
version.json (X-Cache: TCP_REMOTE_HIT) after the origin had rotated to
2.0.1370, pinning the portal's version badge to the old build. The dangerous
variant is worse than a stale badge -- manifest and ZIP are independent cache
entries, so a stale manifest paired with a fresh ZIP fails the bootstrap's
SHA-256 integrity check and the install dies.

Route caching on /agent/* has been disabled (infra side); this commit is the
defense-in-depth half: every upload now carries x-ms-blob-cache-control
no-cache -- the CI Send-Blob (eu and legacy mirror alike), the local build.ps1
counterpart (gitignored, changed in lockstep), and the versions/*.json
manifest publishes. Uniform no-cache rather than an immutable/mutable split
because every blob in the agent container rotates in place, including the
per-line AutopilotMonitor-Agent-v2.zip -- nothing there is safely cacheable.
Existing blobs get the header with the next release's re-upload.

Log/docs updated in url-registry.md; no code paths changed, YAML and ps1
parse-checked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:17:02 +02:00
Oliver Kieselbach bbb55034e3 feat(versioning): one version contract for agent, backend and MCP
All three components now report <major>.<minor>.<build> plus a 7-char commit
and a build timestamp. The backend had been pinned at a hand-written 1.0.0
since inception and starts at 1.5.1000; the MCP server's package.json version
was bumped by hand and starts at 1.5.300, and it now reports its own commit on
/health instead of only the documentation bundle's.

The build number keeps coming from a counter blob reserved before the build
with ETag-CAS, but there is now one implementation instead of two copies:
.github/scripts/Request-BuildNumber.ps1, called by all three workflows and by
the local build.ps1. It lives under .github/ because scripts/Deployment/ is
gitignored (those scripts carry SAS tokens) and would therefore be absent from
a CI checkout; scripts/Build/ and scripts/Release/ are shadowed by the
[Bb]uild/ and [Rr]elease/ artifact ignores.

The counter cannot be replaced by the published manifest, which is the obvious
simplification to reach for: version.json is written only on stable publish, so
two consecutive dev builds would derive the same number and the second would
overwrite the first's artifact. git rev-list --count fails the same way, two
builds of one commit collide.

Both deploy workflows now poll their health endpoint until it reports exactly
the version and commit just deployed, and publish versions/<component>.json
only then. A green deploy step alone means the package was accepted, not that
it is being served -- so a manifest that disagrees with the live endpoint now
means the running instance drifted (rollback, stale revision) rather than a
pipeline that stopped halfway. The agent manifest gains the same commit and
buildUtc fields additively; the bootstrap reads it by field name, so older
installers ignore them, and sha256/bootstrapVersion stay untouched.

Requires the versions container plus AZURE_BLOB_VERSIONS_SAS_TOKEN and the two
seeded counters (backend 999, mcp 299) before the first deploy run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:41:03 +02:00
Oliver Kieselbach 7329f12217 ci(codeql): skip analysis on documentation-only changes
CodeQL analyzes C# and TypeScript, so a commit touching only docs or markdown
cannot change its result - it spent ~10 minutes (8:50 for C#, 1:32 for
TypeScript) re-reporting an unchanged outcome.

paths-ignore on push and pull_request only. The weekly cron stays unfiltered:
it exists to catch advisories published against code that did not change, and
it doubles as the backstop should the filter ever skip too much.

A commit touching docs AND code still runs - GitHub skips only when every
changed path matches the filter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 11:10:18 +02:00
Oliver Kieselbach c9a9df99f1 docs(ci): describe what the CI workflow actually does
The header claimed it "runs the full test suite" - it also typechecks the web
and MCP projects and fails on drift between rules/guardrails.json and the
generated rule files. It also called itself "the gate that decides whether a
change is allowed to be correct", which was both garbled and false: main is
deliberately unprotected, so a red run reports rather than blocks.

Comment only, no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 11:10:17 +02:00
Oliver Kieselbach fa640ebe61 fix(security): resolve CodeQL log-forging findings and suppress by-design queries
Bucket B (real fixes) + Bucket A (config) of the CodeQL alert triage.

Log-forging (cs/log-forging, 8 sinks): add Helpers/LogSanitizer that strips
CR/LF and other control characters, and apply it to the logged copy of every
user-controlled value in AuthenticationMiddleware, PolicyEnforcementMiddleware
(Invoke + DecideAsync), McpQuotaEnforcementMiddleware and TableUserUsageRepository.
Only the logged value is sanitized — routing, authorization and storage keys are
untouched, so tracing fidelity for legitimate values (paths, UPNs, GUIDs) is
preserved. Structured logging already isolated the fields; this closes the CRLF
vector as defense-in-depth and clears the query.

Three genuine JS/TS fixes:
- guardValidation.ts: replace the no-op `.replace(/%/g, "%")` with a real
  escapeRegExp() — the author intended to escape; this also closes the latent
  metacharacter gap (js/identity-replacement).
- access-guard.ts: use a `%s` format argument instead of interpolating the UPN
  into the console.error format string (js/tainted-format-string).
- docs-corpus.ts: strip HTML tags with a loop-until-stable stripHtmlTags() so the
  sanitizer is robust and recognized as complete (js/incomplete-multi-char-sanitization).

CodeQL config: filter the two queries that fire structurally on this codebase,
where the flagged behavior is the feature — cs/exposure-of-sensitive-information
(audit/notification logs legitimately record the actor UPN / recipient email) and
cs/cleartext-storage-of-sensitive-information (rate limiters key on the trusted
client IP). Default suites stay active; genuine one-off false positives from other
queries are dismissed individually so those queries stay sharp.

Tests: Backend xUnit 3110 (+9 LogSanitizer), MCP vitest 19 (+1 tag-strip),
Web vitest 49, all tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 01:38:47 +02:00
Oliver Kieselbach cbda62d131 Merge branch 'main' of https://github.com/okieselbach/Autopilot-Monitor 2026-07-22 01:19:36 +02:00
Oliver Kieselbach 3951000b40 ci(deploy-web): skip SWA deploy when token absent instead of failing
Dependabot PRs run without repository secrets, so AZURE_SWA_DEPLOY_TOKEN is
empty and Azure/static-web-apps-deploy exits with an error, painting a red
"Build and Deploy Job" check on every web-touching Dependabot PR. Setting
skip_deploy_on_missing_secrets makes the action skip the deploy (green) when
the token is missing; real pushes to main still carry the token and deploy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 01:19:26 +02:00
dependabot[bot] 3ed21c37d1 chore(deps): bump actions/upload-artifact from 6 to 7 (#102)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 01:08:23 +02:00
dependabot[bot] 4a1194569e chore(deps): bump actions/checkout from 5 to 7 (#91)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 01:07:14 +02:00
dependabot[bot] d0794fc7c5 chore(deps): bump actions/setup-node from 5 to 7 (#90)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 01:06:47 +02:00
dependabot[bot] f34c9d2322 chore(deps): bump github/codeql-action from 3 to 4 (#89)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 01:06:44 +02:00
dependabot[bot] 380a8417c8 chore(deps): bump actions/setup-dotnet from 5 to 6 (#88)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5 to 6.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 01:06:40 +02:00
Oliver Kieselbach 63826a4869 chore(dependabot): stop auto-PRing npm major bumps
Group config already batches minor/patch; majors fall outside the group and
each opens its own PR (zod 4, typescript 7, transformers 4, msal-react 5,
react-leaflet 5, react 19, @types/node 26, eslint-config-next 16 — all
breaking). Ignore version-update:semver-major for both npm ecosystems so
majors are reviewed deliberately instead of flooding the PR list. Security
updates are exempt from this filter, so vulnerability fixes still flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 01:03:22 +02:00
Oliver Kieselbach 3f9576d4a9 fix(agent): enforce the gather-rule allowlist in every collector
LogParserCollector and EventLogCollector called no guard at all, while
File/Json/Xml did — and rules/gather-rules.md already promised customers
that log-parser paths were allowlisted. A tenant admin could author a
logparser rule against C:\Users via POST /api/rules/gather and have up to
1000 lines uploaded as telemetry, bypassing the hard C:\Users block the
same page guarantees; eventlog could read any channel, Security included.
The backend validates no collectorType, target, or parameters, so the
agent is the only place this can be enforced.

Both now guard. logparser cannot check rule.Target directly (GetFullPath
throws on a wildcard), so it checks the directory for wildcard targets and
re-checks each resolved path. Event log channels get an allowlist matched
on a "/" boundary. Hard-blocked channels live in a C# constant rather than
guardrails.json: a parse error drops the allowlists to empty, which is
fail-closed, but would have lifted a block sourced from the same file.

Also fixes an inert timeout in CommandCollector: ReadToEnd() blocks until
the pipe closes, so WaitForExit(30000) was only reached once the process
had already exited and a hung command pinned its worker indefinitely.

Adds the first real CI gate. No workflow ran tests on a pull request — the
deploy workflows build without testing and build-agent.yml covered only
DecisionCore.Tests on manual dispatch — while trust/security-faq.md
already told customers automated tests run in CI. ci.yml now runs all six
suites on every PR and merge, plus a job that fails on generated-guardrail
drift, alongside CodeQL and Dependabot. OfficeBinaryWatcherTests is marked
SerialThreading; its bounded re-probe races the shared ThreadPool and
failed once under full-suite load.

Verified: 3101 backend, 1911 agent V2, 474 decision core, 44 dialog, 572
web, 447 MCP. The timeout fix is verified by runtime, not assertion — a
Start-Sleep 120 command now returns after ~30s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 00:28:04 +02:00
Oliver Kieselbach 63ea2e3cee feat(mcp): search_docs — customer documentation as a third search corpus
The MCP server could answer telemetry questions ("why did this session fail")
but not product ones ("how do I deploy the agent", "where is my data stored").
Those answers already exist in the autopilotmonitor-docs bundle; search_docs
makes them semantically searchable.

Deliberately a separate index from search_knowledge: rules and prose answer
different questions, and sharing one topK would dilute both. Registered for
every role — published documentation carries no tenant data — and skipped
entirely when no corpus was baked in, so a doc-less build advertises no
broken tool.

Corpus handling:
- Chunked at h2/h3 and <details>/<summary>, each chunk prefixed with a
  "Page > Section > Subsection" breadcrumb so a mid-page hit is self-describing.
- Newlines are normalized before any parsing. This is a correctness
  precondition, not cosmetics: the bundle ships CRLF and JS `.` does not match
  \r, so /^(#{2,3})\s+(.*)$/ matched zero headings — silently, degrading every
  page to unlabelled paragraph splits.
- Two size exceptions: tables are never split (a split table leaves `| --- |`
  rows with no header), and unbroken changelog lists split at line level (left
  whole, their tails fall outside the model window and become unsearchable).
- Short sections are kept — a one-line answer is often the best hit, and the
  breadcrumb supplies the context a short body lacks.

Build and delivery:
- Docs are embedded at image build time into an optional third
  PrecomputedSection. Optional so a checkout without the docs repo does not
  invalidate the rules and event-type vectors; the reverse (server has docs,
  file does not) is staleness and is rejected.
- The deploy workflow checks the public docs repo into the build context and
  passes its SHA as DOCS_COMMIT, which /health reports alongside the chunk
  count — editing the docs does not update a running server, so staleness is
  made checkable rather than memorable.
- Embeddings are rounded to 6 decimals on serialize: 55% smaller index (2.63
  vs 5.88 MB) for a ~1e-5 norm error against a 1e-3 tolerance. indexPrecomputed
  now asserts unit norm, since the hydrate path never checked it.

Infra: the Bicep template was two changes behind the live deployment (EU
cutover and ACR hardening, both applied directly in Azure). Reconciled against
the running resources — managed-identity image pull replaces the admin
credentials and the acr-password secret — and infra/ is no longer gitignored so
it stops drifting. Container raised to 0.5 vCPU / 1.0 GiB for the cold-start
query-embedder load. The template does not model the mcp.autopilotmonitor.com
custom domain, so a full deployment would remove it; the header warns about it.

Verified: 428 tests pass; 45 pages produce 324 chunks; sample queries hit the
right page top-1/2; all 8 derived URLs return 200 live; the precompute output
is accepted by the boot validator and a mutated corpus is rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 19:48:28 +02:00
Oliver Kieselbach e2d78c6b06 chore(ci): rename SWA workflow to deploy-web.yml with neutral deploy-token secret
The brave-grass filename and secret name referred to the pre-migration
Static Web App hostname. Workflow now matches the deploy-backend/-mcp
naming scheme and uses AZURE_SWA_DEPLOY_TOKEN (already set to the new
SWA's token). The old secret is removed after the first green run.
2026-07-18 16:51:39 +02:00
Oliver Kieselbach b1a8e371ad chore(build): retarget DecisionCore.Tests to net10.0, drop 8.0.x SDK from agent CI
Nothing required net8.0 -- the test runner just predated the .NET 10
migration. Agent projects stay net48 (Intune/OOBE constraint).
474/474 tests green on net10.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:01:39 +02:00
Oliver Kieselbach ac31ee980e feat(ci): mirror agent artifacts to legacy storage (transition until download.-alias migration; fail-soft, counter stays on eu only) 2026-07-18 15:57:40 +02:00
Oliver Kieselbach 1bf352542d fix(ci): agent version MERGE targets the eu table endpoint
The AdminConfiguration table SAS is issued for autopilotmonitoreu; the
MERGE URL still pointed at the legacy account and failed auth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:11:54 +02:00
Oliver Kieselbach e2d711dc30 Merge branch 'migration/net10' 2026-07-18 14:03:29 +02:00
Oliver Kieselbach dd39205486 feat(migration): pivot global resource names to eu suffix
Cross-tenant re-claim of recently deleted global names is denied by
Azure by design (dangling-DNS/subdomain-takeover protection). New
tenant resources therefore use eu-suffixed names; the old names live
on in the old subscription (storage 'autopilotmonitor' recreated as
legacy bootstrap-URL keepalive, FuncApp/ACR kept as tombstones).

- API base URL -> autopilotmonitor-api-eu.azurewebsites.net
  (Shared Constants, web CSP + /go fallback, MCP default)
- Agent blob container + build counter -> autopilotmonitoreu
- deploy-backend app-name, deploy-mcp ACR -> eu names
- Bootstrap script URLs stay on the legacy storage (customers
  migrate once to a domain alias later)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 14:02:55 +02:00
Oliver Kieselbach 7377eb9e4d fix(ci): use current repo slug in attestation verify hints
gh attestation verify only passes with the repo's current name
(okieselbach/AutopilotMonitor); the signed workflow identity carries it,
and the old dashed name fails identity matching even though git-remote
redirects still work. Verified against the first attested dry-run ZIP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:22:15 +02:00
Oliver Kieselbach b2b99e78b4 fix(ci): tolerate leading '?' in SAS token secrets
First real run of build-agent.yml failed the counter CAS write with
"AuthenticationFailed ... sp is mandatory": the AZURE_BLOB_AGENT_SAS_TOKEN
secret stores the query with its leading '?', and the workflow prepends
another one, so Azure saw '?sp' as the first parameter name. The pre-existing
upload step had the same latent bug -- it had just never run. Normalize with
TrimStart('?') at all three usage sites (counter, blob upload, table merge).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 11:00:10 +02:00
Oliver Kieselbach 6d7783566f feat(ci): shared build-counter blob, CI build parity, ZIP provenance attestation
Build numbers now come from a public counter blob (buildcounter-v2.txt,
reserve-before-build with ETag-CAS) shared by local build.ps1 and CI, so the
two paths can never mint the same number. The CI step that committed
buildcounter.txt bumps to main is gone; the repo file is now a gitignored
offline fallback for -SkipUpload smoke builds only.

CI parity fixes vs the local build:
- ship SummaryDialog.exe.config in the ZIP (WPF AppContextSwitchOverrides;
  without it the dialog dies with 0x8007045A when spawned from SYSTEM)
- verify EXE version == manifest == reserved build number before zipping
- update the AdminConfiguration hash-oracle only on publish_as_stable

CI-built release ZIPs now carry a GitHub Artifact Attestation (Sigstore
keyless provenance, actions/attest-build-provenance@v4).

Doc: docs/agent/build-counter-blob.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 10:49:18 +02:00
Oliver Kieselbach 7521158e65 backend: bump to .NET 10 (dotnet-isolated 10.0) for the Flex recreate
TargetFramework net8.0 -> net10.0 (Functions + Tests), setup-dotnet 8.0.x -> 10.0.x. JwtBearer 8.0.11 -> 10.0.10 (Microsoft.Identity.Web 4.3.0 requires >= 10.0.0 on net10). Drops the System.Formats.Asn1 / System.Security.Cryptography.Xml vuln-override pins - superseded by the net10 framework versions (NU1510). Build clean, 2937/2937 backend tests green. Merge on cutover Saturday BEFORE running deploy-backend (the new Flex app is created as dotnet-isolated 10.0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 21:34:22 +02:00
Oliver Kieselbach f0a5e550d3 ci: switch Azure deploy login to secret-less OIDC (federated credential)
deploy-backend + deploy-mcp now authenticate via azure/login OIDC (client-id/tenant-id/subscription-id + id-token permission) instead of the AZURE_CREDENTIALS service-principal secret. Adds temporary oidc-login-test workflow to smoke-test the federated login. Federated credential 'github-main' (repo:okieselbach/Autopilot-Monitor:ref:refs/heads/main) created on the deploy app registration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 17:47:25 +02:00
Oliver Kieselbach 6529d972cd ci: bump checkout to v5 in SWA workflow too (missed in cb67e8c2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:52:42 +02:00
Oliver Kieselbach cb67e8c2f0 ci: bump actions to node24-ready versions ahead of June 16 forced default
GitHub forces all JS actions onto Node 24 starting 2026-06-16 and
removes Node 20 from runners on 2026-09-16. Bump every affected action
to the first major that officially declares node24, avoiding majors
with functional changes (checkout v6, upload-artifact v7):

  actions/checkout            v4 -> v5
  azure/login                 v2 -> v3
  actions/setup-dotnet        v4 -> v5
  actions/setup-node          v4 -> v5
  actions/upload-artifact     v4 -> v6 (v5 still defaulted to node20)
  softprops/action-gh-release v2 -> v3
  stefanzweifel/git-auto-commit-action v5 -> v7 (v7 reverted the v6
    input removals; only standard inputs used here anyway)

Azure/functions-action@v1 (floating tag, node24 since v1.5.4) and
Azure/static-web-apps-deploy@v1 (Docker action) need no change.
All target tags verified to exist via git ls-remote.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:52:01 +02:00
Oliver Kieselbach d51bde6017 refactor(release): parametric per-line agent build pipeline
Replaces V1/V2-hardcoded build + bootstrap with a single parametric
pipeline that scales to V3+ without rewrites. Full architecture in
.claude/plans/v2-cutover.md.

Stable namespace (Bootstrap-side, never renamed): generic Bootstrap
script, agent owns its --install defaults so it works for any line.
Versioned namespace (per-line, SelfUpdater-side): isolated *-v{N}.*
blobs; SelfUpdater never crosses lines or touches stable.

Backend GetAgentConfig dispatches per X-Agent-Version-Major to
AdminConfiguration.GetAgentLine(int) -> LatestAgentV{N}* fields.
Future V3 = add field set + switch arm. TableConfigRepository.Map
falls back to legacy unsuffixed columns for transparent migration.

V1 SelfUpdater now reads version-v1.json (would have been clobbered
by the V2 cutover otherwise). V2 agent owns its own 600s TenantId-wait
default so the Bootstrap calls --install plain.

Tests: 3289 across 6 suites, all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 19:36:53 +02:00
Oliver Kieselbach 7abbd59333 revert: roll back .NET 10 upgrade — Flex stamp missing runtime
Reverts:
- 8dacce5c "chore(backend): bump optional NuGet patches + fix SYSLIB0057"
- 2f68fe69 "chore(backend): upgrade Functions from .NET 8 to .NET 10 isolated"

After deploy succeeded and the Flex Consumption stack was switched to
.NET 10 Isolated in the portal, the dotnet-isolated worker died on every
boot with: dotnet exited with code 150 (FrameworkMissingFailure) and
"Exceeded language worker restart retry count for runtime:dotnet-isolated".
The deployed package was correct (TFM net10.0, FrameworkReference 10.0.0,
all package bumps applied) and the runtime stack property was set to
dotnet-isolated 10.0, but the .NET 10 runtime is not present on the
Flex Consumption stamp in West Europe even though Microsoft lists
DOTNET-ISOLATED|10.0 as supported. Restart and stop+start did not help.

Recovery sequence:
1. Stack switched back to .NET 8 Isolated (manual portal action).
2. Re-deployed last known-good net8.0 commit 637bd9a1 via temp branch
   rollback-net8 (now safe to delete).
3. /api/health back to 200.

Follow-ups:
- Re-apply security fixes (DataProtection 10.0.7 pin GHSA-9mv3-2cwr-p262,
  SYSLIB0057 X509CertificateLoader migration) on net8.0 in a separate PR.
- Open Microsoft support: ".NET 10 Isolated worker on Flex Consumption
  WestEurope fails with FrameworkMissingFailure despite stack listed as
  supported."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:42:56 +02:00
Oliver Kieselbach 2f68fe69c0 chore(backend): upgrade Functions from .NET 8 to .NET 10 isolated
- Target framework: net8.0 -> net10.0 (Functions + Tests)
- Microsoft.Identity.Web 4.3.0 -> 4.8.0 (transitively pulls AspNetCore 10.x)
- Microsoft.AspNetCore.Authentication.JwtBearer 8.0.11 -> 10.0.7
- Microsoft.Extensions.Http.Polly 8.0.25 -> 10.0.7
- Pin Microsoft.AspNetCore.DataProtection 10.0.7 to override transitive
  10.0.0 (GHSA-9mv3-2cwr-p262 critical, GHSA-37gx-xxp4-5rgx +
  GHSA-w3x6-4m5h-cxqf high in Cryptography.Xml)
- Add global.json pinning SDK 10.0.x latestFeature, allowPrerelease=false
- CI deploy-backend workflow: setup-dotnet 8.0.x -> 10.0.x ga

Tests: 781/781 passing on net10.0.

Runtime stack switch on the Function App ("Stack settings" in portal:
.NET 8 Isolated -> .NET 10 Isolated) must be done manually after deploy.

Out of scope (carry as follow-ups):
- SYSLIB0057 (X509Certificate2 ctor obsolete) in CertificateValidator.cs:69
- Optional bumps surfaced by 'list package --outdated':
  Azure.Identity 1.19->1.21, Azure.Storage.Blobs 12.22->12.27,
  Azure.Data.Tables 12.9->12.11, SignalR.Management 1.29->1.33,
  Newtonsoft.Json 13.0.3->13.0.4. Major bumps deferred:
  ApplicationInsights.WorkerService 2.x->3.x, Resend 0.2->0.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:08:28 +02:00
Oliver Kieselbach 42ff4284d8 added version info to backend 2026-04-22 19:36:19 +02:00
Oliver Kieselbach 09899a90ac ci(web): allow workflow_dispatch to run the deploy job
The build_and_deploy_job's if-guard accepted push + non-closed PR
events only, so manually triggering the Static Web Apps workflow via
`gh workflow run` ran but skipped both jobs (conclusion=skipped).
After the V2-refactor merge to main (commit 62c0c990), the automatic
push-triggered run did not materialise on the merge commit — likely
a path-filter + merge-commit edge case on GitHub's side — leaving
no way to ship the web changes without either another path-touching
push or hand-editing the workflow.

Adding `github.event_name == 'workflow_dispatch'` to the if-guard
keeps the close-PR job untouched while making manual runs a reliable
escape hatch for future merge-commit oddities.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 15:54:48 +02:00
Oliver Kieselbach e9fcb85797 feat: added LatestVersions api and bootstrapper version handling and hints (badge, docs) 2026-04-09 19:50:13 +02:00
Oliver Kieselbach 66d9795105 fix post-config integrity check: use separate EXE hash instead of ZIP hash
The post-config binary integrity check was comparing the running EXE's
SHA-256 against LatestAgentSha256 which is the hash of the ZIP package —
these can never match. Introduces LatestAgentExeSha256 as a separate hash
computed from AutopilotMonitor.Agent.exe at build time, flowing through
AdminConfiguration → AgentConfigResponse → MonitoringService.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 19:49:20 +02:00
Oliver Kieselbach 55b077659d bug fix bootstrap 2026-03-31 23:30:43 +02:00
Oliver Kieselbach 1a89080f0f agent docs updates 2026-03-31 21:25:51 +02:00
Oliver Kieselbach e7e23f8243 fix merge call 2026-03-29 22:59:20 +02:00
Oliver Kieselbach d1ffac14bc hardened integrity check of agent via SHA256 from version.json and agent config (download and runtime check) 2026-03-29 22:56:11 +02:00
Oliver Kieselbach 8a4a7cf255 Remote MCP server: Streamable HTTP transport, Container App deployment, MCP user access control
- MCP server: stdio → Streamable HTTP (Express) with /health endpoint
- Auth: device code flow → token pass-through (JWT from Claude Code client)
- Dockerfile (multi-stage, node:22-slim) + .dockerignore
- Bicep template: ACR + Container App Environment + Container App (scale-to-zero)
- GitHub Actions: deploy-mcp.yml workflow, all backend workflows migrated to SP auth
- Removed unused dev/main deploy workflows
- Backend: McpAccessPolicy enum (Disabled/WhitelistOnly/AllMembers)
- Backend: McpUsers table + McpUserService (5min cache) + McpUserFunction (CRUD + access check)
- Backend: 6 endpoints registered in EndpointAccessPolicyCatalog
- Web UI: MCP Users management section at /settings/access/mcp-users
- Web UI: user list, add/remove/enable/disable, policy info banner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:09:58 +01:00
Claude 50cc268738 feat: add SummaryDialog build and Azure Blob Storage upload to agent workflow
- Build SummaryDialog (WPF) alongside Agent and bundle into same ZIP
- Upload AutopilotMonitor-Agent.zip + version.json to Azure Blob Storage
  (matches bootstrap script download URL pattern)
- Compute Content-MD5 header for ZIP integrity validation
- New workflow input to toggle blob upload independently from release

Requires AZURE_BLOB_AGENT_SAS_TOKEN secret for blob storage access.

https://claude.ai/code/session_01QiuThUkeSEbiapFpTY9yRp
2026-03-26 18:15:08 +00:00
Claude ddc0328922 feat: add GitHub Actions workflow for Agent build & release
On-demand workflow (workflow_dispatch) that:
- Builds the .NET Framework 4.8 Agent on Windows runner
- Auto-increments build counter or accepts manual override
- Creates a ZIP artifact with all build outputs + version.json
- Optionally creates a GitHub Release with the artifacts
- Commits updated buildcounter.txt back to the repo

https://claude.ai/code/session_01QiuThUkeSEbiapFpTY9yRp
2026-03-26 18:11:08 +00:00
Oliver Kieselbach ffc2f1f67f Remove submodules: true from web deploy workflows
The private submodule (.claude/commands) is not needed for builds
and was causing CI failures since the runner has no access to it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:15:00 +01:00
Oliver Kieselbach 53a3768cdf added valid return codes 2026-03-20 08:21:04 +01:00
Oliver Kieselbach 39d888cfb1 SEO optimizations 2026-03-20 08:07:17 +01:00
Oliver Kieselbach 6f483863d5 updated workflows 2026-03-19 23:13:13 +01:00
Oliver Kieselbach 5e99c96821 added dev workfows 2026-03-17 18:00:07 +01:00
Oliver Kieselbach 91d46e5cde added backend workflow 2026-03-13 23:35:30 +01:00