From dcfe8bbca8026190375084f7cf548c3465b83738 Mon Sep 17 00:00:00 2001 From: Jerry Xiao Date: Sun, 12 Jul 2026 22:36:56 -0500 Subject: [PATCH 1/8] feat: scaffold OSS performance gateway --- .cargo/config.toml | 2 + .editorconfig | 15 + .github/ISSUE_TEMPLATE/bug.yml | 36 ++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature.yml | 37 ++ .github/dependabot.yml | 10 + .github/pull_request_template.md | 24 + .github/workflows/ci.yml | 46 ++ .gitignore | 21 + AGENTS.md | 31 ++ CODE_OF_CONDUCT.md | 20 + CONTRIBUTING.md | 21 + Cargo.lock | 231 ++++++++++ Cargo.toml | 36 ++ GOVERNANCE.md | 5 + LICENSE | 201 ++++++++ NOTICE | 4 + README.md | 122 +++++ SECURITY.md | 21 + SUPPORT.md | 7 + apps/broker/Cargo.toml | 10 + apps/broker/src/main.rs | 5 + apps/cli/Cargo.toml | 10 + apps/cli/src/main.rs | 23 + apps/experiment-runner/Cargo.toml | 10 + apps/experiment-runner/src/main.rs | 5 + apps/gateway/Cargo.toml | 10 + apps/gateway/src/main.rs | 5 + apps/watchdog/Cargo.toml | 10 + apps/watchdog/src/main.rs | 5 + .../dotnet/libre-hardware-monitor/README.md | 5 + clippy.toml | 1 + crates/contracts/Cargo.toml | 15 + crates/contracts/src/lib.rs | 92 ++++ crates/provider-sdk/Cargo.toml | 14 + crates/provider-sdk/src/lib.rs | 58 +++ deny.toml | 22 + docs/ARCHITECTURE.md | 37 ++ docs/IMPLEMENTATION_PLAN.md | 115 +++++ docs/adr/0001-rust-first.md | 19 + docs/implementation-plan.html | 432 ++++++++++++++++++ docs/providers/README.md | 18 + docs/threat-model/README.md | 31 ++ packaging/windows/README.md | 5 + policies/conflicts/default.toml | 10 + policies/registry-catalog/README.md | 3 + policies/risk/default.toml | 16 + profiles/README.md | 3 + rust-toolchain.toml | 4 + rustfmt.toml | 3 + schemas/capability.schema.json | 19 + schemas/sidecar.schema.json | 21 + sidecars/mock-provider/Cargo.toml | 15 + sidecars/mock-provider/src/main.rs | 106 +++++ xtask/Cargo.toml | 11 + xtask/src/main.rs | 25 + 56 files changed, 2088 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 GOVERNANCE.md create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 SUPPORT.md create mode 100644 apps/broker/Cargo.toml create mode 100644 apps/broker/src/main.rs create mode 100644 apps/cli/Cargo.toml create mode 100644 apps/cli/src/main.rs create mode 100644 apps/experiment-runner/Cargo.toml create mode 100644 apps/experiment-runner/src/main.rs create mode 100644 apps/gateway/Cargo.toml create mode 100644 apps/gateway/src/main.rs create mode 100644 apps/watchdog/Cargo.toml create mode 100644 apps/watchdog/src/main.rs create mode 100644 bridges/dotnet/libre-hardware-monitor/README.md create mode 100644 clippy.toml create mode 100644 crates/contracts/Cargo.toml create mode 100644 crates/contracts/src/lib.rs create mode 100644 crates/provider-sdk/Cargo.toml create mode 100644 crates/provider-sdk/src/lib.rs create mode 100644 deny.toml create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/IMPLEMENTATION_PLAN.md create mode 100644 docs/adr/0001-rust-first.md create mode 100644 docs/implementation-plan.html create mode 100644 docs/providers/README.md create mode 100644 docs/threat-model/README.md create mode 100644 packaging/windows/README.md create mode 100644 policies/conflicts/default.toml create mode 100644 policies/registry-catalog/README.md create mode 100644 policies/risk/default.toml create mode 100644 profiles/README.md create mode 100644 rust-toolchain.toml create mode 100644 rustfmt.toml create mode 100644 schemas/capability.schema.json create mode 100644 schemas/sidecar.schema.json create mode 100644 sidecars/mock-provider/Cargo.toml create mode 100644 sidecars/mock-provider/src/main.rs create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..35049cb --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..aba7630 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.rs] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..ba76fea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,36 @@ +name: Bug report +description: Report a reproducible problem that is not security-sensitive. +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: Do not disclose privilege-boundary or unsafe hardware-control vulnerabilities here. Use private vulnerability reporting. + - type: textarea + id: summary + attributes: + label: Summary + description: What happened, and what did you expect? + validations: + required: true + - type: input + id: revision + attributes: + label: Revision + placeholder: Commit SHA + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction + description: Minimal steps, logs, and relevant provider versions. + validations: + required: true + - type: textarea + id: environment + attributes: + label: Environment + description: OS build, CPU, GPU, motherboard, drivers, and installed providers. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0baf118 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security report + url: https://github.com/undeemed/fpsmaxxing/security/advisories/new + about: Report privilege, hardware-safety, or rollback vulnerabilities privately. diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..14c54ab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,37 @@ +name: Feature request +description: Propose a capability, provider, evaluator, or safety improvement. +title: "feat: " +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What measurable user problem should this solve? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed approach + description: Describe the capability boundary rather than a raw command. + validations: + required: true + - type: dropdown + id: risk + attributes: + label: Expected risk class + options: + - Read-only + - Reversible + - Approval required + - Denied or unknown + validations: + required: true + - type: textarea + id: rollback + attributes: + label: Verification and rollback + description: How will the system prove the effect and restore the previous state? + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c7ecf5e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..04feec4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,24 @@ +## Why + + + +## What changed + + + +## Safety and rollback + +- Risk class: +- Privilege required: +- Snapshot: +- Verification: +- Lease/rollback: +- Conflicts: + +## Validation + +- [ ] `cargo fmt --all -- --check` +- [ ] `cargo check --workspace --all-targets` +- [ ] `cargo clippy --workspace --all-targets -- -D warnings` +- [ ] `cargo test --workspace` +- [ ] Documentation updated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c944a8d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + rust: + name: Rust (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Install pinned Rust toolchain + run: rustup show + - name: Cache Cargo artifacts + uses: Swatinem/rust-cache@v2 + - name: Check formatting + run: cargo fmt --all -- --check + - name: Check workspace + run: cargo check --workspace --all-targets --locked + - name: Run Clippy + run: cargo clippy --workspace --all-targets --locked -- -D warnings + - name: Run tests + run: cargo test --workspace --locked + + schemas: + name: JSON schemas + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Parse schemas + shell: python + run: | + import json + from pathlib import Path + for path in Path("schemas").glob("*.json"): + json.loads(path.read_text()) + print(f"validated {path}") diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3d5cd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +/target/ +/dist/ +/.idea/ +/.vscode/ +*.user +*.suo +*.swp +*.tmp +*.log +.DS_Store +Thumbs.db + +# Local machine state must never enter source control. +/.fpsmaxxing/ +/.env +/.env.* +!.env.example + +# .NET bridge output. +**/bin/ +**/obj/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..98e438e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,31 @@ +# Agent instructions + +Read `docs/IMPLEMENTATION_PLAN.md`, `docs/ARCHITECTURE.md`, and `docs/threat-model/README.md` before changing architecture or privileged code. + +## Non-negotiable boundaries + +- The LLM-facing gateway is not privileged. +- The broker never accepts raw shell commands, arbitrary Registry paths, arbitrary memory addresses, MSR indices, port I/O, or firmware variables. +- Every mutating capability requires a pre-state snapshot, bounded parameters, a verification probe, a TTL lease, and rollback. +- Unknown provider versions and unknown hardware fail closed. +- Only one provider may own a knob at a time. +- The watchdog must be able to restore state without the gateway or LLM. +- BIOS, voltage, firmware flashing, Secure Boot, TPM, boot settings, and kernel patching are out of scope unless the implementation plan is explicitly amended. + +## Repository rules + +- Keep shared wire types in `crates/contracts`. +- Keep provider lifecycle behavior in `crates/provider-sdk`. +- Put provider-specific code in one `sidecars/` package; sidecars may not import each other. +- Put non-Rust compatibility processes under `bridges/` and isolate them behind the sidecar protocol. +- Do not vendor third-party binaries without confirmed redistribution rights. +- Add tests before enabling a real write path. + +## Required validation + +```bash +cargo fmt --all -- --check +cargo check --workspace --all-targets +cargo clippy --workspace --all-targets -- -D warnings +cargo test --workspace +``` diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..86956ac --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,20 @@ +# Code of conduct + +## Our pledge + +We pledge to make participation in FPSMaxxing a harassment-free experience for everyone, regardless of background, identity, experience, or level of contribution. + +## Expected behavior + +- Be respectful, specific, and constructive. +- Critique ideas and implementations, not people. +- Prioritize safety, reproducibility, and evidence over hype. +- Accept responsibility for mistakes and help repair their impact. + +## Unacceptable behavior + +Harassment, threats, discrimination, sexualized conduct, sustained disruption, publication of private information, or encouraging unsafe use of privileged hardware controls is not acceptable. + +## Enforcement + +Report conduct concerns privately to the maintainer through the contact methods on the maintainer's GitHub profile. Maintainers may remove content, restrict participation, or ban contributors when necessary to protect the community. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5bbeaaa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing + +FPSMaxxing welcomes focused, reviewable contributions that preserve the project's safety boundaries. + +## Before opening a pull request + +1. Read `AGENTS.md` and `docs/IMPLEMENTATION_PLAN.md`. +2. Open an issue before introducing a new privileged operation or changing a wire contract. +3. Keep provider-specific code inside its provider package. +4. Include negative-path and rollback tests for every state-changing operation. +5. Run the full validation suite documented in `AGENTS.md`. + +## Pull request expectations + +- Explain the user problem and why the change belongs in FPSMaxxing. +- List affected capabilities and risk classes. +- Describe pre-state capture, verification, lease expiry, and rollback. +- Document supported operating-system, provider, and hardware versions. +- Include evidence for any performance claim. + +By submitting a contribution, you agree that it is licensed under Apache-2.0 as described in the repository license. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fef9b11 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,231 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "fpsmaxxing-broker" +version = "0.1.0" + +[[package]] +name = "fpsmaxxing-cli" +version = "0.1.0" + +[[package]] +name = "fpsmaxxing-contracts" +version = "0.1.0" +dependencies = [ + "schemars", + "serde", + "serde_json", +] + +[[package]] +name = "fpsmaxxing-experiment-runner" +version = "0.1.0" + +[[package]] +name = "fpsmaxxing-gateway" +version = "0.1.0" + +[[package]] +name = "fpsmaxxing-mock-provider" +version = "0.1.0" +dependencies = [ + "fpsmaxxing-contracts", + "fpsmaxxing-provider-sdk", + "serde_json", +] + +[[package]] +name = "fpsmaxxing-provider-sdk" +version = "0.1.0" +dependencies = [ + "fpsmaxxing-contracts", + "thiserror", +] + +[[package]] +name = "fpsmaxxing-watchdog" +version = "0.1.0" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "xtask" +version = "0.1.0" + +[[package]] +name = "zmij" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2f034a4bebf216c9e4b7083603e024cf930873fd67830cfb083c9fa33129d9" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c64f4a6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,36 @@ +[workspace] +resolver = "3" +members = [ + "apps/broker", + "apps/cli", + "apps/experiment-runner", + "apps/gateway", + "apps/watchdog", + "crates/contracts", + "crates/provider-sdk", + "sidecars/mock-provider", + "xtask", +] + +[workspace.package] +version = "0.1.0" +edition = "2024" +license = "Apache-2.0" +repository = "https://github.com/undeemed/fpsmaxxing" +rust-version = "1.96" + +[workspace.dependencies] +fpsmaxxing-contracts = { path = "crates/contracts" } +fpsmaxxing-provider-sdk = { path = "crates/provider-sdk" } +schemars = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" + +[workspace.lints.rust] +unsafe_code = "forbid" +missing_docs = "warn" + +[workspace.lints.clippy] +all = "warn" +pedantic = "warn" diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000..9e1125c --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,5 @@ +# Governance + +FPSMaxxing is currently maintained by Jerry Xiao. Decisions are made in public issues and pull requests whenever possible. + +Maintainers may reject changes that expand privilege, weaken rollback, rely on undocumented hardware behavior, make unsupported performance claims, or create redistribution risk. Governance will evolve as the contributor community grows. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5d26c98 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Jerry Xiao + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..4310a17 --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +FPSMaxxing +Copyright 2026 Jerry Xiao + +This product is licensed under the Apache License, Version 2.0. diff --git a/README.md b/README.md new file mode 100644 index 0000000..66463c2 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +# FPSMaxxing + +**Autonomous, measurement-driven system performance tuning.** + +FPSMaxxing is an open-source Rust control plane for using an AI coding agent or LLM—such as Claude, Codex, or another MCP client—to improve gaming FPS, frame pacing, system latency, thermals, power efficiency, and compute throughput through bounded, reversible experiments. + +> [!IMPORTANT] +> FPSMaxxing is currently an architecture scaffold. It does **not** perform real hardware writes, overclock a GPU, edit BIOS settings, or modify the Windows Registry yet. + +## Why FPSMaxxing? + +Existing tools already know how to control parts of a PC: + +- Process Lasso manages process priorities, CPU sets, affinities, and power profiles. +- Fan Control manages fan curves and thermal response. +- PresentMon measures frame times, latency, and rendering performance. +- LibreHardwareMonitor reads clocks, temperatures, fan speeds, loads, and power. +- NVML and AMD SMI expose supported GPU telemetry and controls. +- Windows APIs expose power policy and documented Registry settings. + +FPSMaxxing is intended to connect those control planes to a reproducible research loop: + +```text +observe → propose → validate → snapshot → apply → benchmark → keep or rollback +``` + +The LLM proposes an experiment. Deterministic policy, broker, provider, watchdog, and measurement components decide whether the experiment is allowed and whether its measured result should be retained. + +## Design principles + +- **The model is never the root process.** Privileged changes pass through a small Rust broker. +- **Capabilities, not shell commands.** Agents call typed operations with bounded parameters. +- **Every write is leased.** Changes require a snapshot, verification probe, deadline, and rollback path. +- **One owner per knob.** Conflicting tuning applications cannot fight over the same setting. +- **Measurement beats folklore.** Changes survive only when repeated workload tests show a practical improvement. +- **Fast loops stay local.** Drivers and dedicated tools control millisecond-to-second behavior; the LLM operates at experiment cadence. + +## Planned integrations + +| Area | Initial provider | Intended use | +| --- | --- | --- | +| Process scheduling | Process Lasso | CPU affinity, CPU sets, priorities, process power profiles | +| Frame performance | PresentMon | FPS, frame time, latency, GPU telemetry | +| Hardware telemetry | LibreHardwareMonitor bridge | Temperatures, clocks, loads, power, fan RPM | +| Fan control | Fan Control | Complete, reviewed thermal profiles | +| NVIDIA GPU | NVML | Supported clock and power-limit operations | +| AMD GPU | AMD SMI | Supported telemetry and control operations | +| Windows power | Native Windows APIs | Cloned power schemes and processor policy | +| Registry | Curated catalog | Documented, typed, versioned, reversible settings only | + +BIOS changes, voltage changes, raw MSR/PCI/EC access, firmware flashing, and arbitrary Registry paths are explicitly outside the first release. + +## Repository status + +The repository currently includes: + +- A Rust 2024 Cargo workspace +- Shared capability and provider contracts +- A provider SDK lifecycle +- A working mock sidecar with snapshot/apply/verify/rollback tests +- Scaffolds for the gateway, privileged broker, watchdog, experiment runner, and CLI +- OSS governance, security policy, issue templates, and CI +- The complete [implementation plan](docs/IMPLEMENTATION_PLAN.md) + +Try the safe scaffold: + +```bash +cargo test --workspace +cargo run -p fpsmaxxing-cli -- doctor +cargo run -p fpsmaxxing-mock-provider +``` + +## Architecture + +```text +LLM / MCP client + │ + ▼ +Rust gateway ──► policy engine ──► privileged broker ──► provider sidecars + ▲ │ + │ ▼ +telemetry normalizer ◄──────────── independent watchdog + │ + ▼ +experiment journal and benchmark decision gate +``` + +See [Architecture](docs/ARCHITECTURE.md), [Implementation Plan](docs/IMPLEMENTATION_PLAN.md), and [Agent Instructions](AGENTS.md). + +## Frequently asked questions + +### Can Claude optimize my PC for higher FPS? + +That is the intended workflow. A Claude or Codex agent should be able to inspect available capabilities, propose a bounded change, run a controlled game or benchmark workload, and keep the change only when frame time, latency, thermals, and correctness remain within policy. That closed loop is planned but not implemented yet. + +### Can an AI safely overclock a GPU? + +FPSMaxxing will not expose arbitrary voltage or register writes. Planned GPU operations use supported vendor APIs, query device limits first, require rollback, and run under an independent thermal watchdog. Persistent or high-risk operations require explicit approval. + +### Is this an AI BIOS optimizer? + +Not initially. Consumer BIOS settings are vendor-specific and can make a machine unbootable. Server firmware may eventually be supported through Redfish and boot recovery, but firmware autonomy is deliberately deferred. + +### Does FPSMaxxing replace Process Lasso, Fan Control, or MSI Afterburner? + +No. It is an orchestration and measurement layer. Where a stable third-party control plane exists, FPSMaxxing should integrate with it instead of reimplementing its hardware logic. + +### Will it blindly apply Windows performance tweaks from the internet? + +No. Registry and power changes must come from a curated catalog containing the supported Windows versions, exact value type, permitted values, verification method, risk class, and rollback procedure. + +## Contributing + +Read [CONTRIBUTING.md](CONTRIBUTING.md) and the current [implementation plan](docs/IMPLEMENTATION_PLAN.md) before starting a provider. Security-sensitive changes require tests for denial, timeout, lease expiry, verification failure, and rollback. + +## Security + +Do not open public issues for vulnerabilities that could enable arbitrary privileged execution or unsafe hardware writes. Follow [SECURITY.md](SECURITY.md). + +## License + +Licensed under the [Apache License 2.0](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..7933162 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security policy + +## Supported versions + +FPSMaxxing is pre-release software. Only the latest commit on the default branch is considered for security fixes. + +## Reporting a vulnerability + +Use GitHub's private vulnerability reporting feature for this repository. Do not create a public issue for vulnerabilities involving: + +- privilege-boundary bypasses; +- arbitrary command, Registry, memory, MSR, PCI, EC, or firmware access; +- missing rollback or lease enforcement; +- sidecar impersonation or named-pipe authorization; +- unsafe fan, clock, power, or thermal behavior. + +Include reproduction steps, the affected commit, impact, and any proposed mitigation. Please do not test against machines or accounts you do not own or control. + +## Security design + +The gateway is unprivileged. Mutations require a typed capability, policy approval, provider-specific bounds, a durable journal, verification, and independent rollback. See `docs/threat-model/README.md`. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..d1ca27d --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,7 @@ +# Support + +- Use GitHub Discussions for design questions and usage ideas once discussions are enabled. +- Use issues for reproducible bugs and scoped feature requests. +- Use private vulnerability reporting for security-sensitive reports. + +The project is an early scaffold and does not yet provide production support or safe real-hardware tuning. diff --git a/apps/broker/Cargo.toml b/apps/broker/Cargo.toml new file mode 100644 index 0000000..50271b8 --- /dev/null +++ b/apps/broker/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fpsmaxxing-broker" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true diff --git a/apps/broker/src/main.rs b/apps/broker/src/main.rs new file mode 100644 index 0000000..4cce700 --- /dev/null +++ b/apps/broker/src/main.rs @@ -0,0 +1,5 @@ +//! Privileged provider broker process. + +fn main() { + println!("fpsmaxxing-broker: scaffold ready; no privileged actions are enabled"); +} diff --git a/apps/cli/Cargo.toml b/apps/cli/Cargo.toml new file mode 100644 index 0000000..5d93457 --- /dev/null +++ b/apps/cli/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fpsmaxxing-cli" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true diff --git a/apps/cli/src/main.rs b/apps/cli/src/main.rs new file mode 100644 index 0000000..0efcb5c --- /dev/null +++ b/apps/cli/src/main.rs @@ -0,0 +1,23 @@ +//! Operator CLI for diagnostics and local development. + +use std::env; + +fn main() { + match env::args().nth(1).as_deref() { + Some("doctor") => doctor(), + Some(command) => { + eprintln!("unknown command: {command}"); + eprintln!("usage: fpsmaxxing-cli doctor"); + std::process::exit(2); + } + None => println!("fpsmaxxing-cli\n\nusage: fpsmaxxing-cli doctor"), + } +} + +fn doctor() { + println!("FPSMaxxing scaffold diagnostics"); + println!(" contracts: ready"); + println!(" provider SDK: ready"); + println!(" hardware writes: disabled"); + println!(" status: architecture scaffold only"); +} diff --git a/apps/experiment-runner/Cargo.toml b/apps/experiment-runner/Cargo.toml new file mode 100644 index 0000000..c44c156 --- /dev/null +++ b/apps/experiment-runner/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fpsmaxxing-experiment-runner" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true diff --git a/apps/experiment-runner/src/main.rs b/apps/experiment-runner/src/main.rs new file mode 100644 index 0000000..1296f77 --- /dev/null +++ b/apps/experiment-runner/src/main.rs @@ -0,0 +1,5 @@ +//! Deterministic performance experiment runner. + +fn main() { + println!("fpsmaxxing-experiment-runner: scaffold ready; no benchmark configured"); +} diff --git a/apps/gateway/Cargo.toml b/apps/gateway/Cargo.toml new file mode 100644 index 0000000..d21c1ac --- /dev/null +++ b/apps/gateway/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fpsmaxxing-gateway" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true diff --git a/apps/gateway/src/main.rs b/apps/gateway/src/main.rs new file mode 100644 index 0000000..b958b68 --- /dev/null +++ b/apps/gateway/src/main.rs @@ -0,0 +1,5 @@ +//! LLM-facing MCP gateway process. + +fn main() { + println!("fpsmaxxing-gateway: scaffold ready; MCP transport is not implemented yet"); +} diff --git a/apps/watchdog/Cargo.toml b/apps/watchdog/Cargo.toml new file mode 100644 index 0000000..e1eb548 --- /dev/null +++ b/apps/watchdog/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fpsmaxxing-watchdog" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true diff --git a/apps/watchdog/src/main.rs b/apps/watchdog/src/main.rs new file mode 100644 index 0000000..9cc29b4 --- /dev/null +++ b/apps/watchdog/src/main.rs @@ -0,0 +1,5 @@ +//! Independent lease and safety watchdog. + +fn main() { + println!("fpsmaxxing-watchdog: scaffold ready; no leases are active"); +} diff --git a/bridges/dotnet/libre-hardware-monitor/README.md b/bridges/dotnet/libre-hardware-monitor/README.md new file mode 100644 index 0000000..bfc43a5 --- /dev/null +++ b/bridges/dotnet/libre-hardware-monitor/README.md @@ -0,0 +1,5 @@ +# LibreHardwareMonitor bridge + +This directory is reserved for the only planned non-Rust component in v1: a minimal .NET process that exposes normalized LibreHardwareMonitor telemetry through the versioned sidecar protocol. + +The bridge must remain read-only initially and must not run inside the privileged broker. diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..5bb56f2 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +msrv = "1.96" diff --git a/crates/contracts/Cargo.toml b/crates/contracts/Cargo.toml new file mode 100644 index 0000000..625e613 --- /dev/null +++ b/crates/contracts/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fpsmaxxing-contracts" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +schemars.workspace = true +serde.workspace = true +serde_json.workspace = true + +[lints] +workspace = true diff --git a/crates/contracts/src/lib.rs b/crates/contracts/src/lib.rs new file mode 100644 index 0000000..68e15f3 --- /dev/null +++ b/crates/contracts/src/lib.rs @@ -0,0 +1,92 @@ +//! Versioned data contracts shared by `FPSMaxxing` applications and sidecars. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +/// The safety classification attached to a capability or requested change. +#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum RiskClass { + /// Observation only; no system state changes. + ReadOnly, + /// Temporary and mechanically reversible without a reboot. + Reversible, + /// Requires an explicit approval policy before application. + ApprovalRequired, + /// Intentionally unavailable to autonomous agents. + Denied, +} + +/// How long a capability's effect can survive. +#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum Persistence { + /// The operation does not change state. + None, + /// The operation expires with its lease or provider process. + Leased, + /// The operation remains until it is explicitly reverted. + Persistent, + /// The operation requires a reboot to become active. + RebootRequired, +} + +/// A semantic operation advertised by a provider. +#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +pub struct CapabilityDescriptor { + /// Stable capability name, such as `process.cpu-affinity`. + pub id: String, + /// Human-readable summary for operators and agents. + pub description: String, + /// Safety class enforced by the policy engine. + pub risk: RiskClass, + /// Persistence behavior of successful changes. + pub persistence: Persistence, + /// JSON Schema describing accepted parameters. + pub input_schema: Value, +} + +/// Provider identity and the capabilities it advertises. +#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +pub struct ProviderManifest { + /// Stable provider identifier. + pub id: String, + /// Provider protocol version. + pub protocol_version: u32, + /// Targets supported by this build. + pub targets: Vec, + /// Semantic capabilities exposed by the provider. + pub capabilities: Vec, +} + +/// A requested provider change after policy validation. +#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +pub struct ChangeRequest { + /// Capability being invoked. + pub capability_id: String, + /// Capability-specific parameters. + pub parameters: Value, + /// Automatic rollback deadline in seconds. + pub lease_seconds: u64, +} + +/// Opaque provider state captured before a change. +#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +pub struct StateSnapshot { + /// Provider that produced the snapshot. + pub provider_id: String, + /// Provider-specific state required for rollback. + pub state: Value, +} + +#[cfg(test)] +mod tests { + use super::{Persistence, RiskClass}; + + #[test] + fn risk_classes_are_not_implicitly_ordered() { + assert_ne!(RiskClass::ReadOnly, RiskClass::Reversible); + assert_ne!(Persistence::Leased, Persistence::Persistent); + } +} diff --git a/crates/provider-sdk/Cargo.toml b/crates/provider-sdk/Cargo.toml new file mode 100644 index 0000000..088bcaa --- /dev/null +++ b/crates/provider-sdk/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "fpsmaxxing-provider-sdk" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +fpsmaxxing-contracts.workspace = true +thiserror.workspace = true + +[lints] +workspace = true diff --git a/crates/provider-sdk/src/lib.rs b/crates/provider-sdk/src/lib.rs new file mode 100644 index 0000000..b33fe9b --- /dev/null +++ b/crates/provider-sdk/src/lib.rs @@ -0,0 +1,58 @@ +//! Minimal provider lifecycle used by native sidecars. + +use fpsmaxxing_contracts::{ChangeRequest, ProviderManifest, StateSnapshot}; +use thiserror::Error; + +/// Errors returned through the provider boundary. +#[derive(Debug, Error)] +pub enum ProviderError { + /// The requested capability is not implemented by this provider. + #[error("unsupported capability: {0}")] + UnsupportedCapability(String), + /// The provider rejected invalid or unsafe input. + #[error("invalid request: {0}")] + InvalidRequest(String), + /// The external service or vendor API is unavailable. + #[error("provider unavailable: {0}")] + Unavailable(String), + /// Rollback could not restore the captured state. + #[error("rollback failed: {0}")] + RollbackFailed(String), +} + +/// Contract implemented by every provider sidecar. +pub trait Provider { + /// Returns provider identity, supported targets, and capabilities. + fn manifest(&self) -> ProviderManifest; + + /// Captures the exact state needed to reverse the next operation. + /// + /// # Errors + /// + /// Returns [`ProviderError`] when the external provider is unavailable or + /// cannot produce a complete rollback snapshot. + fn snapshot(&self) -> Result; + + /// Applies a previously policy-validated request. + /// + /// # Errors + /// + /// Returns [`ProviderError`] when the capability is unsupported, input is + /// invalid, or the external provider rejects the requested change. + fn apply(&mut self, request: &ChangeRequest) -> Result<(), ProviderError>; + + /// Confirms that the requested effect is observable. + /// + /// # Errors + /// + /// Returns [`ProviderError`] when the provider state cannot be observed. + fn verify(&self, request: &ChangeRequest) -> Result; + + /// Restores a state captured by [`Provider::snapshot`]. + /// + /// # Errors + /// + /// Returns [`ProviderError`] when the snapshot is invalid or restoration + /// fails. + fn rollback(&mut self, snapshot: &StateSnapshot) -> Result<(), ProviderError>; +} diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..8399bfb --- /dev/null +++ b/deny.toml @@ -0,0 +1,22 @@ +[advisories] +unmaintained = "workspace" + +[licenses] +confidence-threshold = 0.93 +allow = [ + "Apache-2.0", + "BSD-2-Clause", + "BSD-3-Clause", + "ISC", + "MIT", + "Unicode-3.0", + "Zlib", +] + +[bans] +multiple-versions = "warn" +wildcards = "deny" + +[sources] +unknown-registry = "deny" +unknown-git = "deny" diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..f11ea3f --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,37 @@ +# Architecture + +FPSMaxxing separates reasoning, policy, privilege, hardware integration, measurement, and recovery. + +## Processes + +### Gateway + +The unprivileged MCP server translates agent tool calls into typed capability requests. It may inspect public capability metadata and telemetry but cannot directly mutate the host. + +### Broker + +The privileged Windows service accepts authenticated local requests from the gateway, revalidates policy, journals the transaction, and supervises provider sidecars. It exposes no raw command or memory primitive. + +### Watchdog + +The independent watchdog owns lease deadlines and emergency rollback. It must restore state without the gateway, agent, or experiment runner. + +### Experiment runner + +The runner controls workload setup, warmup, repeated measurements, cooldown, correctness checks, and promotion decisions. Evaluator code is outside the LLM's writable surface. + +### Provider sidecars + +Each sidecar integrates exactly one service or vendor API. Sidecars advertise semantic capabilities and implement snapshot, apply, verify, and rollback. They do not make cross-provider decisions. + +## Dependency rule + +Dependencies point inward: + +```text +apps and sidecars → shared crates +shared crates ↛ apps or sidecars +sidecar A ↛ sidecar B +``` + +Non-Rust compatibility code lives under `bridges/` and speaks the same versioned sidecar protocol. diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..7417db0 --- /dev/null +++ b/docs/IMPLEMENTATION_PLAN.md @@ -0,0 +1,115 @@ +# FPSMaxxing implementation plan + +## Goal + +Build a Windows-first, Rust-first performance-control gateway that lets an LLM inspect, propose, measure, and roll back safe system tuning experiments through typed capabilities rather than administrator shell access. + +The first prototype targets one workstation. Third-party tools remain user-installed and are detected at runtime. BIOS writes, voltage changes, arbitrary Registry edits, raw hardware access, and firmware flashing are excluded. + +## Architecture + +```text +LLM / outer research loop + │ typed MCP tools + ▼ +Unprivileged gateway + │ validated request + ▼ +Capability registry + policy engine + │ authenticated local IPC + ▼ +Privileged broker ──► provider sidecars ──► supported vendor/service APIs + │ + ├──► durable experiment journal + └──► independent watchdog and rollback +``` + +The LLM proposes declarative experiments. It never receives administrator credentials, a generic shell, arbitrary Registry access, or raw device primitives. + +## Language and repository decisions + +- Rust 2024 edition on the pinned stable toolchain +- Cargo workspace and `xtask` orchestration +- Tokio for async services and Windows named pipes +- Official Rust MCP SDK for the northbound agent interface +- Microsoft Rust crates for focused Windows Registry and service APIs +- Serde and Schemars for wire contracts and JSON Schema +- SQLite journal through Rusqlite +- One isolated C# bridge for LibreHardwareMonitor +- No Node, Turborepo, Bazel, or Tauri in v1 + +Source is organized by deployable role and provider. Release artifacts are assembled by OS and target triple. + +## Provider contract + +Every provider implements the same lifecycle: + +1. `discover` — version, health, target, capabilities, valid ranges, conflicts. +2. `snapshot` — capture the exact state required for reversal. +3. `preview` — produce a typed, human-readable change description. +4. `apply` — perform one policy-approved bounded change. +5. `verify` — read back provider state and observe the real system effect. +6. `rollback` — restore the snapshot without consulting the LLM. + +Every mutation has a TTL lease. If the gateway, workload, or agent dies, the watchdog restores the prior state. + +## Integration order + +| Order | Provider | Initial boundary | Promotion evidence | +| --- | --- | --- | --- | +| 1 | LibreHardwareMonitor bridge | Read-only telemetry | Stable normalized samples under workload | +| 1 | PresentMon | Frame and latency metrics | Correlated frame and hardware timeline | +| 2 | Process Lasso | Switch reviewed profiles | Apply, verify, and restore baseline profile | +| 2 | Windows power APIs | Clone and activate project-owned schemes | User's original scheme remains untouched | +| 3 | Registry provider | Curated catalog only | Exact type, view, value, effect, and rollback verified | +| 3 | NVML / AMD SMI | Supported clocks and power limits | Limits queried; default reset and watchdog tested | +| 3 | Fan Control | Switch complete JSON profiles | Fallback profile activates after process loss | + +## One-hour AI implementation sprint + +The following times are autonomous coding budgets assuming dependencies, administrator access, and target hardware are already available. Benchmark duration, thermal soak, reboots, signing, licensing, and human approvals are validation time and remain evidence-gated. + +| Phase | Budget | Output | Exit gate | +| --- | ---: | --- | --- | +| Workspace and contract | 5 min | Cargo workspace, schemas, provider traits, CI | Mock provider passes full lifecycle | +| Read-only alpha | 10 min | MCP gateway, journal, telemetry adapters | Reproducible baseline report | +| Safe actuators | 15 min | Broker, IPC ACLs, Process Lasso, power, Registry leases | Gateway termination restores state | +| Hardware adapters | 10 min | GPU and fan profile providers, watchdog | Fault injection restores defaults | +| Experiment engine | 10 min | Baseline/candidate trials and decision gate | Trial replays without chat history | +| Hardening | 10 min | Reboot recovery, tamper tests, installer and diagnostics | Uninstall restores the baseline | + +## Experiment loop + +```text +observe + → propose a typed hypothesis + → intersect with policy and provider limits + → snapshot + → apply one change with a TTL + → warm up + → run repeated baseline/candidate measurements + → enforce correctness, temperature, power, and error constraints + → keep or roll back +``` + +Use the LLM for hypothesis generation and explanation. Use deterministic search and statistics for numeric optimization. + +## Safety invariants + +- No generic shell or raw Registry path reaches the broker. +- Unknown hardware and provider versions fail closed. +- Every write has a snapshot, TTL, verification probe, and rollback method. +- Only one provider owns a setting at a time. +- Critical thermal limits remain owned by hardware, firmware, and drivers. +- The watchdog is independent of the gateway and experiment runner. +- Persistent BIOS, security, voltage, and firmware operations require a future design amendment. + +## MVP acceptance criteria + +- Installed providers and capabilities are discovered. +- A complete baseline snapshot is journaled. +- Process Lasso profile apply and restore are demonstrated. +- One power, Registry, GPU, and fan transaction passes its contract tests. +- Crash, timeout, telemetry loss, and reboot rollback are demonstrated. +- One measured experiment is promoted or rejected using the immutable evaluator. +- Uninstall removes project-created services, profiles, rules, and Registry values. diff --git a/docs/adr/0001-rust-first.md b/docs/adr/0001-rust-first.md new file mode 100644 index 0000000..b9fe423 --- /dev/null +++ b/docs/adr/0001-rust-first.md @@ -0,0 +1,19 @@ +# ADR 0001: Rust-first implementation + +- Status: accepted +- Date: 2026-07-12 + +## Decision + +Use Rust for every trusted process and shared library. Use a C# sidecar only where the selected hardware library is natively .NET. + +## Rationale + +Rust provides a strong fit for long-running services, explicit error handling, cross-platform native binaries, constrained unsafe code, and shared types across IPC, policy, and providers. A Cargo workspace is sufficient for the initial monorepo. Additional build systems would duplicate Cargo's dependency and task graph before the repository is large enough to benefit. + +## Consequences + +- Unsafe Rust is forbidden workspace-wide by default. +- Windows APIs use focused Microsoft Rust crates where possible. +- Sidecar protocols remain language-neutral JSON Schema contracts. +- A future UI may use Tauri, but no TypeScript toolchain is introduced before a UI exists. diff --git a/docs/implementation-plan.html b/docs/implementation-plan.html new file mode 100644 index 0000000..8895023 --- /dev/null +++ b/docs/implementation-plan.html @@ -0,0 +1,432 @@ + + + + + + Performance Gateway — Implementation Plan + + + + + + + +
+ + +
+
+
+

Implementation plan · Windows-first prototype

+
+
+

Build the highways before the research loop.

+

A local capability gateway normalizes Process Lasso, Fan Control, PresentMon, LibreHardwareMonitor, GPU APIs, Windows power policy, and curated Registry tweaks. The LLM proposes experiments; deterministic services execute and reverse them.

+
+
+
Target
1 workstation
Local-only Windows prototype
+
AI implementation target
≈60 minutes
Code, tests, and first vertical slice
+
Implementation language
Rust-first
One isolated .NET hardware bridge
+
Validation clock
Evidence-based
Benchmark, reboot, and thermal time stays real
+
v1 risk ceiling
Reversible
No BIOS, voltage, or raw kernel writes
+
+
+
+
+
+
+ +
+
+
In scope

Capability gateway

Typed discovery, snapshot, apply, verify, observe, lease, and rollback operations.

+
In scope

Provider adapters

Profile-based integrations first; direct vendor APIs where they are stable and supported.

+
Deferred

Firmware autonomy

BIOS, voltage, arbitrary Registry, raw MSR/PCI/EC access, and firmware flashing.

+
+ +
+
+

01 · Architecture

+

Where trust and privilege live

+

The LLM never receives administrator credentials or raw command execution. A local gateway exposes semantic tools; a separately installed Windows service owns privileged provider calls and rollback.

+
+
+flowchart TD + LLM["LLM / outer research loop"] -->|"typed MCP tools"| GW["Local capability gateway\nnon-admin process"] + GW --> REG["Capability registry + policy engine"] + REG -->|"validated request over ACL'd named pipe"| BROKER["Privileged broker\nWindows service"] + BROKER --> PL["Process Lasso adapter"] + BROKER --> FC["Fan Control adapter"] + BROKER --> PWR["Windows power + Registry adapters"] + BROKER --> GPU["NVML / AMD SMI adapter"] + PM["PresentMon"] --> OBS["Telemetry normalizer"] + LHM["LibreHardwareMonitor"] --> OBS + PL --> OBS + FC --> OBS + GPU --> OBS + OBS --> DB[("SQLite experiment journal")] + OBS --> GW + WATCH["Independent watchdog service"] -->|"trip / TTL expiry"| BROKER + BROKER --> DB +
+ +
+ +
+

02 · Provider contract

+

Every highway has the same shape

+
+

Discover

Version, health, available capabilities, valid ranges, persistence, conflicts.

+

Transact

Snapshot, preview, apply one bounded change, attach a TTL lease.

+

Prove

Read back provider state and verify the actual OS or hardware effect.

+

Recover

Restore the exact snapshot without consulting the LLM.

+
+
+
capabilities.list()
+
change.preview(capability, parameters)
+
change.apply(preview_id, lease_seconds)
+
change.verify(change_id)
+
change.rollback(change_id)
+
telemetry.query(metric_set, window)
+
+
+ +
+
+
+

03 · Language decision

+

Rust owns every trusted boundary

+
+ Decision locked +
+

Use stable Rust with edition 2024 for the gateway, privileged broker, watchdog, experiment runner, CLI, shared crates, and native provider sidecars. Keep a single C# bridge for LibreHardwareMonitor because its supported integration surface is a .NET library; isolate it behind the same sidecar protocol rather than moving .NET into the trusted broker.

+ +
+ + + + + + + + + + + + + + +
ConcernDecisionReason
Async runtimetokioShared runtime for MCP, provider supervision, telemetry streams, timers, and Windows named pipes.
LLM interfacermcpThe official Rust MCP SDK already uses Tokio, Serde, and generated JSON Schemas.
Windows APIswindows-registry, windows-services, windowsPrefer Microsoft's focused crates; enable only required Win32 namespaces for lower API surface.
Local privileged IPCtokio named pipes + length-prefixed JSONInspectable v1 protocol with named-pipe ACLs; no localhost TCP listener.
Contractsserde + schemarsOne Rust type generates wire-format JSON Schema and validates MCP inputs.
JournalrusqliteSmall synchronous transaction surface behind one journal actor; bundled SQLite for predictable installs.
CLI and diagnosticsclap + tracingTyped commands and structured diagnostics without a separate logging stack.
Errorsthiserror; anyhow at binary edgesLibraries preserve typed failure semantics; applications add operational context.
Build orchestrationCargo workspace + xtaskNo Bazel or Turborepo in v1; Cargo builds Rust and xtask coordinates .NET, packaging, schemas, and release assembly.
Desktop UIDeferred; Tauri-compatibleDo not add TypeScript until a UI is actually required.
+
+ +
+ +
+

04 · Monorepo structure

+

Provider-first source, target-first artifacts

+

Use top-level folders for deployable roles, shared libraries, and isolated providers. Keep each sidecar together by provider; put OS-specific implementations inside it. Package output by target triple so installers still receive a clean Windows/Linux layout.

+ +
+
+
+ Option A +

OS-first source

+

sidecars/windows/process-lasso

+

Simple packaging view, but duplicates shared providers and makes NVIDIA or future cross-platform integrations awkward.

+
+
+
+
+ Option B +

Capability-first only

+

providers/process/cpu-affinity

+

Clean logical model, but hides executable, privilege, packaging, and failure-isolation boundaries.

+
+
+
+
+ Recommended +

Hybrid

+

sidecars/nvidia/platform/windows

+

Provider ownership stays cohesive while target-specific code and release artifacts remain explicit.

+
+
+
+ +
+
+
+
+
+

Proposed repository tree

+

Cargo workspace with one isolated .NET bridge.

+
+ proposed-tree.txt +
+
+
+
+ +
+

Source boundary

apps/ contains shipped Rust binaries. sidecars/ contains provider-specific Rust processes. crates/ can never call vendor tools directly.

+

Sidecar rule

Every sidecar owns a manifest, source, platform shims, fixtures, and contract tests. No provider-specific types leak into shared contracts.

+

Artifact rule

CI emits dist/<target-triple>/sidecars/<provider>, following the target-specific binary model used by Tauri sidecars.

+

Dependency rule

Dependencies point inward: apps and sidecars may depend on packages; packages never depend on apps or sidecars; sidecars never import one another.

+
+
+ +
+ + + + + + + + + + + + + +
DirectoryOwnsMust not own
apps/Gateway, broker, watchdog, runner, CLI, future desktop UIProvider-specific protocol parsing
sidecars/One external service/vendor integration per process boundaryCross-provider policy or experiment decisions
crates/Contracts, policy, IPC, telemetry, journal, SDK, testkitOS calls, admin writes, vendor binaries
bridges/Non-Rust compatibility processes, beginning with LibreHardwareMonitorPolicy, privilege escalation, or direct broker linkage
schemas/Versioned wire and manifest schemasRuntime code
policies/Risk rules, conflicts, curated Registry catalogSecrets or machine-local state
profiles/Reviewed provider profiles and safe baselinesUser-generated runtime snapshots
packaging/Installers, service definitions, target manifestsSource-of-truth provider logic
third-party/Licenses, notices, checksums, acquisition metadataVendor binaries without redistribution rights
+
+ + +
+ +
+

05 · Integration order

+

Prove read paths before write paths

+

Third-party applications remain user-installed during the prototype. Adapters discover supported versions and refuse unknown contracts.

+
+ + + + + + + + + + + +
OrderProviderFirst integrationControl boundaryExit test
1LibreHardwareMonitorRead-only .NET libraryTemperatures, clocks, fan RPM, loads, powerStable normalized samples for a 30-minute workload
1PresentMonService/API or console CSVFrame time, latency, FPS, GPU telemetryCorrelate workload metrics with hardware samples
2Process LassoList/switch prebuilt profiles; ingest logsAffinity, CPU sets, priorities, process power profileApply, verify, and restore baseline profile
2Windows powerNative Power APIsClone and activate controlled power schemesNever mutate the user's original scheme
3RegistryCurated tweak catalog onlyExact path/value/type/view; no raw path toolLease expiry restores existence, type, and bytes
3NVIDIA / AMDNVML first; AMD SMI after contract paritySupported clocks and power limits; no voltageProvider reset plus watchdog rollback tested
3Fan ControlSwitch complete JSON configsFan Control owns the fast thermal loopFallback config activates if gateway dies
+
+
+ +
+

06 · Delivery roadmap

+

One-hour AI build sprint with hard promotion gates

+ +
+
Phase 0

Workspace + contract

5 minutes

Create the Cargo workspace, pin the stable toolchain, add CI formatting/lint/audit gates, and freeze provider traits, capability schemas, risk classes, conflict model, experiment record, and error taxonomy.

Gate: a fake Rust sidecar passes discover → snapshot → apply → verify → rollback over the real IPC protocol.

+
Phase 1

Read-only alpha

10 minutes

Build the Rust MCP gateway with rmcp, hardware inventory, telemetry normalizer, rusqlite journal actor, PresentMon sidecar, and the isolated .NET LibreHardwareMonitor bridge.

Gate: reproducible baseline reports with clock, thermal, power, latency, and workload context.

+
Phase 2

Safe actuators

15 minutes

Add the privileged broker, named-pipe ACLs, Process Lasso profile switching, cloned Windows power schemes, curated Registry transactions, and TTL leases.

Gate: killing the gateway always restores every active change.

+
Phase 3

Hardware adapters

10 minutes

Add NVML or AMD SMI and Fan Control profile switching. Introduce exclusive ownership locks and an independent watchdog.

Gate: injected telemetry loss, provider crash, and thermal trip all restore defaults.

+
Phase 4

Experiment engine

10 minutes

Implement baseline, warmup, repeated candidate trials, cooldown, practical-effect threshold, keep/rollback, and profile promotion. Add LLM hypothesis tools only after deterministic runs work.

Gate: the same trial can be replayed from its journal without the original LLM conversation.

+
Phase 5

Hardening

10 minutes

Fault injection, reboot recovery, binary/version verification, tamper tests, least-privilege review, installer/uninstaller, diagnostics bundle, and single-machine pilot.

Gate: uninstall restores baseline and leaves no active rules, services, profiles, or Registry values created by the product.

+
+
+ +
+
+

07 · Safety invariants

+

Rules code cannot override

+
    +
  • 01
    No generic shell or raw Registry path
    Only catalogued semantic capabilities reach the broker.
  • +
  • 02
    Every write has a snapshot and TTL
    If rollback cannot be proven before apply, reject the change.
  • +
  • 03
    Unknown versions fail closed
    Provider discovery never guesses an integration contract.
  • +
  • 04
    Watchdog is independent
    The LLM, gateway, and experiment process cannot disable it.
  • +
  • 05
    One owner per knob
    Conflicting tools block promotion until ownership is resolved.
  • +
+
+
+

08 · Test strategy

+

Verify failure, not only success

+
+
Adapter contract tests
Fake providers and real providers must pass identical lifecycle, idempotency, timeout, snapshot, verification, and rollback tests.
+
Fault injection
Kill gateway, broker client, provider, and workload; corrupt a response; remove telemetry; force a timeout; reboot with an active lease.
+
Hardware-in-the-loop
Fan and GPU tests run only on a sacrificial lab machine with hard temperature limits and physical recovery access.
+
Statistical benchmark tests
Use controlled warmup, repeated baseline/candidate runs, cooldown, correctness checks, and a minimum practical improvement.
+
+
+
+ +
+

09 · MVP acceptance

+

The prototype is done when...

+
+ + + + + + +
+
+ +
+

Review decisions

+

Resolve the prototype shape

+

The recommended defaults are selected. Change them if needed, then queue one consolidated decision for the implementation spec.

+
+
Primary workload

Defines the first immutable evaluator and metrics.

+
First GPU provider

Monitoring can remain cross-vendor even if control begins with one.

+
Third-party delivery

Avoids early packaging and licensing commitments.

+
Medium-risk approval

Low-risk read-only and temporary profile operations remain autonomous.

+
+ Selection stays local until you queue it. + +
+
+
+ +
+

Primary references

+ +
+
+ + + diff --git a/docs/providers/README.md b/docs/providers/README.md new file mode 100644 index 0000000..a566afe --- /dev/null +++ b/docs/providers/README.md @@ -0,0 +1,18 @@ +# Provider development + +Provider sidecars are capability adapters, not autonomous optimizers. + +Each provider must document: + +- supported provider and OS versions; +- discovery and health checks; +- semantic capabilities and parameter bounds; +- required privilege; +- persistence and reboot behavior; +- conflicts and ownership; +- snapshot format; +- verification probe; +- rollback behavior; +- test fixtures and failure injection. + +Start new providers from `sidecars/mock-provider`. diff --git a/docs/threat-model/README.md b/docs/threat-model/README.md new file mode 100644 index 0000000..f45f0df --- /dev/null +++ b/docs/threat-model/README.md @@ -0,0 +1,31 @@ +# Threat model + +## Protected assets + +- Host availability and bootability +- User data and workload correctness +- Registry, power, fan, clock, and process-policy state +- Privileged broker credentials and IPC endpoints +- Experiment history and rollback snapshots + +## Primary threats + +- Prompt injection causing a privileged action +- Sidecar impersonation or protocol confusion +- Parameter smuggling outside provider limits +- Conflicting tools racing over the same setting +- Telemetry loss hiding unsafe temperatures or errors +- Process termination leaving persistent changes behind +- Malicious or incompatible third-party binaries +- Reboot before a candidate configuration is blessed + +## Required mitigations + +- Typed allowlisted capabilities +- Local IPC authentication and ACLs +- Provider version and binary verification +- Policy enforcement in both gateway and broker +- Durable pre-state journal and TTL leases +- Independent watchdog and last-known-good baseline +- Fail-closed behavior on unknown state or missing telemetry +- Hardware-in-the-loop fault tests before enabling writes diff --git a/packaging/windows/README.md b/packaging/windows/README.md new file mode 100644 index 0000000..6d9e995 --- /dev/null +++ b/packaging/windows/README.md @@ -0,0 +1,5 @@ +# Windows packaging + +The Windows package will install the gateway, broker, watchdog, CLI, provider sidecars, schemas, and baseline policies. Third-party applications remain user-installed during the first prototype. + +Packaging must preserve target-specific binaries under `dist//` and provide a complete uninstall rollback test. diff --git a/policies/conflicts/default.toml b/policies/conflicts/default.toml new file mode 100644 index 0000000..7f8c4dd --- /dev/null +++ b/policies/conflicts/default.toml @@ -0,0 +1,10 @@ +# Only one provider may own a conflict group at a time. + +[groups.process-policy] +providers = ["process-lasso", "windows-native-process-policy"] + +[groups.gpu-fan] +providers = ["fan-control", "nvidia", "amd"] + +[groups.power-plan] +providers = ["process-lasso", "windows-power"] diff --git a/policies/registry-catalog/README.md b/policies/registry-catalog/README.md new file mode 100644 index 0000000..c19180d --- /dev/null +++ b/policies/registry-catalog/README.md @@ -0,0 +1,3 @@ +# Registry catalog + +Only documented, versioned Registry settings may be added. Every entry requires an exact hive, path, value name, type, 32/64-bit view, permitted values, supported Windows builds, verification probe, risk class, restart requirement, and rollback behavior. diff --git a/policies/risk/default.toml b/policies/risk/default.toml new file mode 100644 index 0000000..f3f1c22 --- /dev/null +++ b/policies/risk/default.toml @@ -0,0 +1,16 @@ +[risk.read-only] +approval = "automatic" +requires_snapshot = false + +[risk.reversible] +approval = "policy" +requires_snapshot = true +requires_lease = true + +[risk.approval-required] +approval = "interactive" +requires_snapshot = true +requires_lease = true + +[risk.denied] +approval = "never" diff --git a/profiles/README.md b/profiles/README.md new file mode 100644 index 0000000..fbd0721 --- /dev/null +++ b/profiles/README.md @@ -0,0 +1,3 @@ +# Profiles + +Reviewed Process Lasso, Fan Control, and Windows power profiles belong here. Runtime snapshots and machine-local state must not be committed. diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..bf5eb1a --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.96.1" +components = ["clippy", "rustfmt"] +profile = "minimal" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..d1582eb --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +edition = "2024" +newline_style = "Unix" +use_field_init_shorthand = true diff --git a/schemas/capability.schema.json b/schemas/capability.schema.json new file mode 100644 index 0000000..5edf78c --- /dev/null +++ b/schemas/capability.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/undeemed/fpsmaxxing/schemas/capability.schema.json", + "title": "FPSMaxxing capability", + "type": "object", + "additionalProperties": false, + "required": ["id", "description", "risk", "persistence", "input_schema"], + "properties": { + "id": { "type": "string", "pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$" }, + "description": { "type": "string", "minLength": 1 }, + "risk": { + "enum": ["read-only", "reversible", "approval-required", "denied"] + }, + "persistence": { + "enum": ["none", "leased", "persistent", "reboot-required"] + }, + "input_schema": { "type": "object" } + } +} diff --git a/schemas/sidecar.schema.json b/schemas/sidecar.schema.json new file mode 100644 index 0000000..2cdce7a --- /dev/null +++ b/schemas/sidecar.schema.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/undeemed/fpsmaxxing/schemas/sidecar.schema.json", + "title": "FPSMaxxing sidecar manifest", + "type": "object", + "additionalProperties": false, + "required": ["id", "protocol_version", "targets", "capabilities"], + "properties": { + "id": { "type": "string", "pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$" }, + "protocol_version": { "type": "integer", "minimum": 1 }, + "targets": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "capabilities": { + "type": "array", + "items": { "$ref": "capability.schema.json" } + } + } +} diff --git a/sidecars/mock-provider/Cargo.toml b/sidecars/mock-provider/Cargo.toml new file mode 100644 index 0000000..f9942b8 --- /dev/null +++ b/sidecars/mock-provider/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fpsmaxxing-mock-provider" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +fpsmaxxing-contracts.workspace = true +fpsmaxxing-provider-sdk.workspace = true +serde_json.workspace = true + +[lints] +workspace = true diff --git a/sidecars/mock-provider/src/main.rs b/sidecars/mock-provider/src/main.rs new file mode 100644 index 0000000..0ff4d8e --- /dev/null +++ b/sidecars/mock-provider/src/main.rs @@ -0,0 +1,106 @@ +//! Reference sidecar used to validate the provider lifecycle without hardware. + +use fpsmaxxing_contracts::{ + CapabilityDescriptor, ChangeRequest, Persistence, ProviderManifest, RiskClass, StateSnapshot, +}; +use fpsmaxxing_provider_sdk::{Provider, ProviderError}; +use serde_json::{Value, json}; + +struct MockProvider { + value: u64, +} + +impl Provider for MockProvider { + fn manifest(&self) -> ProviderManifest { + ProviderManifest { + id: "mock".to_owned(), + protocol_version: 1, + targets: vec![std::env::consts::OS.to_owned()], + capabilities: vec![CapabilityDescriptor { + id: "mock.value".to_owned(), + description: "Sets an in-memory value for contract testing".to_owned(), + risk: RiskClass::Reversible, + persistence: Persistence::Leased, + input_schema: json!({ + "type": "object", + "required": ["value"], + "properties": { "value": { "type": "integer", "minimum": 0 } } + }), + }], + } + } + + fn snapshot(&self) -> Result { + Ok(StateSnapshot { + provider_id: "mock".to_owned(), + state: json!({ "value": self.value }), + }) + } + + fn apply(&mut self, request: &ChangeRequest) -> Result<(), ProviderError> { + if request.capability_id != "mock.value" { + return Err(ProviderError::UnsupportedCapability( + request.capability_id.clone(), + )); + } + + self.value = request + .parameters + .get("value") + .and_then(Value::as_u64) + .ok_or_else(|| ProviderError::InvalidRequest("value must be a u64".to_owned()))?; + Ok(()) + } + + fn verify(&self, request: &ChangeRequest) -> Result { + Ok(request.parameters.get("value").and_then(Value::as_u64) == Some(self.value)) + } + + fn rollback(&mut self, snapshot: &StateSnapshot) -> Result<(), ProviderError> { + self.value = snapshot + .state + .get("value") + .and_then(Value::as_u64) + .ok_or_else(|| ProviderError::RollbackFailed("snapshot has no value".to_owned()))?; + Ok(()) + } +} + +fn main() -> Result<(), ProviderError> { + let mut provider = MockProvider { value: 0 }; + let snapshot = provider.snapshot()?; + let request = ChangeRequest { + capability_id: "mock.value".to_owned(), + parameters: json!({ "value": 1 }), + lease_seconds: 30, + }; + + provider.apply(&request)?; + assert!(provider.verify(&request)?); + provider.rollback(&snapshot)?; + println!("{}", provider.manifest().id); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn apply_verify_and_rollback() { + let mut provider = MockProvider { value: 7 }; + let snapshot = provider.snapshot().expect("snapshot should succeed"); + let request = ChangeRequest { + capability_id: "mock.value".to_owned(), + parameters: json!({ "value": 42 }), + lease_seconds: 30, + }; + + provider.apply(&request).expect("apply should succeed"); + assert!(provider.verify(&request).expect("verify should succeed")); + provider + .rollback(&snapshot) + .expect("rollback should succeed"); + assert_eq!(provider.value, 7); + } +} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..3bb4745 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "xtask" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +publish = false + +[lints] +workspace = true diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..c15f97e --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,25 @@ +//! Repository automation entrypoint. + +use std::{env, process::Command}; + +fn main() { + match env::args().nth(1).as_deref() { + Some("check") => run("cargo", &["check", "--workspace"]), + Some("test") => run("cargo", &["test", "--workspace"]), + Some(command) => { + eprintln!("unknown xtask: {command}"); + std::process::exit(2); + } + None => println!("usage: cargo xtask "), + } +} + +fn run(program: &str, args: &[&str]) { + let status = Command::new(program) + .args(args) + .status() + .unwrap_or_else(|error| panic!("failed to launch {program}: {error}")); + if !status.success() { + std::process::exit(status.code().unwrap_or(1)); + } +} From 71cf1534f8e63533aeb36293a4453822e3be1e1d Mon Sep 17 00:00:00 2001 From: Jerry Xiao Date: Sun, 12 Jul 2026 23:10:01 -0500 Subject: [PATCH 2/8] no-mistakes(review): enforce cargo-deny in CI and add schema sync tests --- .github/workflows/ci.yml | 10 +- apps/broker/Cargo.toml | 1 + apps/cli/Cargo.toml | 1 + apps/experiment-runner/Cargo.toml | 1 + apps/gateway/Cargo.toml | 1 + apps/watchdog/Cargo.toml | 1 + crates/contracts/Cargo.toml | 1 + crates/contracts/src/lib.rs | 187 ++++++++++++++++++++++++++++- crates/provider-sdk/Cargo.toml | 1 + deny.toml | 1 + sidecars/mock-provider/Cargo.toml | 1 + sidecars/mock-provider/src/main.rs | 4 +- 12 files changed, 202 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c944a8d..5f3af96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install pinned Rust toolchain - run: rustup show + run: rustup toolchain install - name: Cache Cargo artifacts uses: Swatinem/rust-cache@v2 - name: Check formatting @@ -31,6 +31,14 @@ jobs: - name: Run tests run: cargo test --workspace --locked + cargo-deny: + name: Cargo deny + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check advisories, bans, licenses, and sources + uses: EmbarkStudios/cargo-deny-action@v2 + schemas: name: JSON schemas runs-on: ubuntu-latest diff --git a/apps/broker/Cargo.toml b/apps/broker/Cargo.toml index 50271b8..c4d6d6e 100644 --- a/apps/broker/Cargo.toml +++ b/apps/broker/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [lints] workspace = true diff --git a/apps/cli/Cargo.toml b/apps/cli/Cargo.toml index 5d93457..95cf568 100644 --- a/apps/cli/Cargo.toml +++ b/apps/cli/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [lints] workspace = true diff --git a/apps/experiment-runner/Cargo.toml b/apps/experiment-runner/Cargo.toml index c44c156..dc2b8ad 100644 --- a/apps/experiment-runner/Cargo.toml +++ b/apps/experiment-runner/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [lints] workspace = true diff --git a/apps/gateway/Cargo.toml b/apps/gateway/Cargo.toml index d21c1ac..9560637 100644 --- a/apps/gateway/Cargo.toml +++ b/apps/gateway/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [lints] workspace = true diff --git a/apps/watchdog/Cargo.toml b/apps/watchdog/Cargo.toml index e1eb548..e525f82 100644 --- a/apps/watchdog/Cargo.toml +++ b/apps/watchdog/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [lints] workspace = true diff --git a/crates/contracts/Cargo.toml b/crates/contracts/Cargo.toml index 625e613..f81c821 100644 --- a/crates/contracts/Cargo.toml +++ b/crates/contracts/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [dependencies] schemars.workspace = true diff --git a/crates/contracts/src/lib.rs b/crates/contracts/src/lib.rs index 68e15f3..f05a201 100644 --- a/crates/contracts/src/lib.rs +++ b/crates/contracts/src/lib.rs @@ -1,5 +1,7 @@ //! Versioned data contracts shared by `FPSMaxxing` applications and sidecars. +use std::num::NonZeroU32; + use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -34,6 +36,7 @@ pub enum Persistence { /// A semantic operation advertised by a provider. #[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub struct CapabilityDescriptor { /// Stable capability name, such as `process.cpu-affinity`. pub id: String, @@ -49,11 +52,12 @@ pub struct CapabilityDescriptor { /// Provider identity and the capabilities it advertises. #[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub struct ProviderManifest { /// Stable provider identifier. pub id: String, - /// Provider protocol version. - pub protocol_version: u32, + /// Provider protocol version; at least 1, matching the sidecar schema. + pub protocol_version: NonZeroU32, /// Targets supported by this build. pub targets: Vec, /// Semantic capabilities exposed by the provider. @@ -62,6 +66,7 @@ pub struct ProviderManifest { /// A requested provider change after policy validation. #[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub struct ChangeRequest { /// Capability being invoked. pub capability_id: String, @@ -73,6 +78,7 @@ pub struct ChangeRequest { /// Opaque provider state captured before a change. #[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub struct StateSnapshot { /// Provider that produced the snapshot. pub provider_id: String, @@ -82,11 +88,180 @@ pub struct StateSnapshot { #[cfg(test)] mod tests { - use super::{Persistence, RiskClass}; + use std::collections::BTreeSet; + + use serde_json::{Value, json}; + + use super::{CapabilityDescriptor, NonZeroU32, Persistence, ProviderManifest, RiskClass}; + + const CAPABILITY_SCHEMA: &str = include_str!("../../../schemas/capability.schema.json"); + const SIDECAR_SCHEMA: &str = include_str!("../../../schemas/sidecar.schema.json"); + + fn wire_string(value: impl serde::Serialize) -> String { + serde_json::to_value(value) + .expect("serialization should succeed") + .as_str() + .expect("enums should serialize to strings") + .to_owned() + } + + fn string_set(values: &Value) -> BTreeSet { + values + .as_array() + .expect("schema field should be an array") + .iter() + .map(|value| { + value + .as_str() + .expect("schema entries should be strings") + .to_owned() + }) + .collect() + } + + fn sample_capability() -> CapabilityDescriptor { + CapabilityDescriptor { + id: "process.cpu-affinity".to_owned(), + description: "Pins a process to selected cores".to_owned(), + risk: RiskClass::Reversible, + persistence: Persistence::Leased, + input_schema: json!({ "type": "object" }), + } + } + + fn sample_manifest() -> ProviderManifest { + ProviderManifest { + id: "mock".to_owned(), + protocol_version: NonZeroU32::MIN, + targets: vec!["windows".to_owned()], + capabilities: vec![sample_capability()], + } + } + + #[test] + fn enum_wire_strings_match_capability_schema() { + assert_eq!(wire_string(RiskClass::ReadOnly), "read-only"); + assert_eq!(wire_string(RiskClass::Reversible), "reversible"); + assert_eq!( + wire_string(RiskClass::ApprovalRequired), + "approval-required" + ); + assert_eq!(wire_string(RiskClass::Denied), "denied"); + assert_eq!(wire_string(Persistence::None), "none"); + assert_eq!(wire_string(Persistence::Leased), "leased"); + assert_eq!(wire_string(Persistence::Persistent), "persistent"); + assert_eq!(wire_string(Persistence::RebootRequired), "reboot-required"); + + let schema: Value = + serde_json::from_str(CAPABILITY_SCHEMA).expect("capability schema should parse"); + assert_eq!( + string_set(&schema["properties"]["risk"]["enum"]), + [ + RiskClass::ReadOnly, + RiskClass::Reversible, + RiskClass::ApprovalRequired, + RiskClass::Denied, + ] + .map(wire_string) + .into_iter() + .collect() + ); + assert_eq!( + string_set(&schema["properties"]["persistence"]["enum"]), + [ + Persistence::None, + Persistence::Leased, + Persistence::Persistent, + Persistence::RebootRequired, + ] + .map(wire_string) + .into_iter() + .collect() + ); + } + + #[test] + fn capability_fields_match_capability_schema() { + let schema: Value = + serde_json::from_str(CAPABILITY_SCHEMA).expect("capability schema should parse"); + let serialized = + serde_json::to_value(sample_capability()).expect("capability should serialize"); + let fields: BTreeSet = serialized + .as_object() + .expect("capability should serialize to an object") + .keys() + .cloned() + .collect(); + + let properties: BTreeSet = schema["properties"] + .as_object() + .expect("schema should declare properties") + .keys() + .cloned() + .collect(); + assert_eq!(fields, properties); + assert_eq!(fields, string_set(&schema["required"])); + assert_eq!(schema["additionalProperties"], json!(false)); + } + + #[test] + fn manifest_fields_match_sidecar_schema() { + let schema: Value = + serde_json::from_str(SIDECAR_SCHEMA).expect("sidecar schema should parse"); + let serialized = + serde_json::to_value(sample_manifest()).expect("manifest should serialize"); + let fields: BTreeSet = serialized + .as_object() + .expect("manifest should serialize to an object") + .keys() + .cloned() + .collect(); + + let properties: BTreeSet = schema["properties"] + .as_object() + .expect("schema should declare properties") + .keys() + .cloned() + .collect(); + assert_eq!(fields, properties); + assert_eq!(fields, string_set(&schema["required"])); + assert_eq!(schema["additionalProperties"], json!(false)); + } + + #[test] + fn unknown_fields_are_rejected_like_the_schemas() { + let mut serialized = + serde_json::to_value(sample_capability()).expect("capability should serialize"); + serialized["unexpected"] = json!(true); + assert!(serde_json::from_value::(serialized).is_err()); + + let mut serialized = + serde_json::to_value(sample_manifest()).expect("manifest should serialize"); + serialized["unexpected"] = json!(true); + assert!(serde_json::from_value::(serialized).is_err()); + } + + #[test] + fn protocol_version_zero_is_rejected_like_the_schema() { + let schema: Value = + serde_json::from_str(SIDECAR_SCHEMA).expect("sidecar schema should parse"); + assert_eq!( + schema["properties"]["protocol_version"]["minimum"], + json!(1) + ); + + let mut serialized = + serde_json::to_value(sample_manifest()).expect("manifest should serialize"); + serialized["protocol_version"] = json!(0); + assert!(serde_json::from_value::(serialized).is_err()); + } #[test] - fn risk_classes_are_not_implicitly_ordered() { - assert_ne!(RiskClass::ReadOnly, RiskClass::Reversible); - assert_ne!(Persistence::Leased, Persistence::Persistent); + fn wire_types_round_trip() { + let manifest = sample_manifest(); + let serialized = serde_json::to_value(&manifest).expect("manifest should serialize"); + let deserialized: ProviderManifest = + serde_json::from_value(serialized).expect("manifest should deserialize"); + assert_eq!(manifest, deserialized); } } diff --git a/crates/provider-sdk/Cargo.toml b/crates/provider-sdk/Cargo.toml index 088bcaa..59a84c5 100644 --- a/crates/provider-sdk/Cargo.toml +++ b/crates/provider-sdk/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [dependencies] fpsmaxxing-contracts.workspace = true diff --git a/deny.toml b/deny.toml index 8399bfb..ea3d7a8 100644 --- a/deny.toml +++ b/deny.toml @@ -16,6 +16,7 @@ allow = [ [bans] multiple-versions = "warn" wildcards = "deny" +allow-wildcard-paths = true [sources] unknown-registry = "deny" diff --git a/sidecars/mock-provider/Cargo.toml b/sidecars/mock-provider/Cargo.toml index f9942b8..1e328b0 100644 --- a/sidecars/mock-provider/Cargo.toml +++ b/sidecars/mock-provider/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true +publish = false [dependencies] fpsmaxxing-contracts.workspace = true diff --git a/sidecars/mock-provider/src/main.rs b/sidecars/mock-provider/src/main.rs index 0ff4d8e..a177b1a 100644 --- a/sidecars/mock-provider/src/main.rs +++ b/sidecars/mock-provider/src/main.rs @@ -1,5 +1,7 @@ //! Reference sidecar used to validate the provider lifecycle without hardware. +use std::num::NonZeroU32; + use fpsmaxxing_contracts::{ CapabilityDescriptor, ChangeRequest, Persistence, ProviderManifest, RiskClass, StateSnapshot, }; @@ -14,7 +16,7 @@ impl Provider for MockProvider { fn manifest(&self) -> ProviderManifest { ProviderManifest { id: "mock".to_owned(), - protocol_version: 1, + protocol_version: NonZeroU32::MIN, targets: vec![std::env::consts::OS.to_owned()], capabilities: vec![CapabilityDescriptor { id: "mock.value".to_owned(), From a752f21a86c61efc80f0916bec79fc98e2fba474 Mon Sep 17 00:00:00 2001 From: Jerry Xiao Date: Sun, 12 Jul 2026 23:22:24 -0500 Subject: [PATCH 3/8] no-mistakes(document): document cargo-deny in required validation suite --- AGENTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 98e438e..8feec0e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,4 +28,8 @@ cargo fmt --all -- --check cargo check --workspace --all-targets cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace +cargo deny check ``` + +`cargo deny check` requires [cargo-deny](https://github.com/EmbarkStudios/cargo-deny) and enforces the advisory, ban, license, and source policies in `deny.toml`. +CI runs the same checks, plus JSON schema validation, on every push and pull request. From 4c3c3b50b52d61e4eb796889ee6df5d073f0d2d3 Mon Sep 17 00:00:00 2001 From: Jerry Xiao Date: Sun, 12 Jul 2026 23:28:17 -0500 Subject: [PATCH 4/8] fix(ci): force LF checkout via .gitattributes so rustfmt newline check passes on Windows --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf From e0855b72b646347f1e18b8dd0ba79c305dd91e90 Mon Sep 17 00:00:00 2001 From: Jerry Xiao Date: Sun, 12 Jul 2026 23:41:20 -0500 Subject: [PATCH 5/8] docs: organize implementation guidance --- AGENTS.md | 2 +- README.md | 6 ++-- ...ion-plan.html => IMPLEMENTATION_PLAN.html} | 30 ++++++++----------- docs/IMPLEMENTATION_PLAN.md | 2 ++ docs/README.md | 21 +++++++++++++ 5 files changed, 40 insertions(+), 21 deletions(-) rename docs/{implementation-plan.html => IMPLEMENTATION_PLAN.html} (90%) create mode 100644 docs/README.md diff --git a/AGENTS.md b/AGENTS.md index 8feec0e..b1afacb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Agent instructions -Read `docs/IMPLEMENTATION_PLAN.md`, `docs/ARCHITECTURE.md`, and `docs/threat-model/README.md` before changing architecture or privileged code. +Start with `docs/README.md`. Read `docs/IMPLEMENTATION_PLAN.md`, `docs/ARCHITECTURE.md`, and `docs/threat-model/README.md` before changing architecture or privileged code. ## Non-negotiable boundaries diff --git a/README.md b/README.md index 66463c2..a8db66e 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ The repository currently includes: - A working mock sidecar with snapshot/apply/verify/rollback tests - Scaffolds for the gateway, privileged broker, watchdog, experiment runner, and CLI - OSS governance, security policy, issue templates, and CI -- The complete [implementation plan](docs/IMPLEMENTATION_PLAN.md) +- An organized [documentation index](docs/README.md) with architecture, plans, threat model, and provider guides Try the safe scaffold: @@ -85,7 +85,9 @@ telemetry normalizer ◄──────────── independent watchdo experiment journal and benchmark decision gate ``` -See [Architecture](docs/ARCHITECTURE.md), [Implementation Plan](docs/IMPLEMENTATION_PLAN.md), and [Agent Instructions](AGENTS.md). +## Documentation + +Start with the [documentation index](docs/README.md). The core references are the [architecture](docs/ARCHITECTURE.md), implementation plan in [Markdown](docs/IMPLEMENTATION_PLAN.md) or [HTML](docs/IMPLEMENTATION_PLAN.html), [threat model](docs/threat-model/README.md), and [agent instructions](AGENTS.md). ## Frequently asked questions diff --git a/docs/implementation-plan.html b/docs/IMPLEMENTATION_PLAN.html similarity index 90% rename from docs/implementation-plan.html rename to docs/IMPLEMENTATION_PLAN.html index 8895023..0a2e7f2 100644 --- a/docs/implementation-plan.html +++ b/docs/IMPLEMENTATION_PLAN.html @@ -21,9 +21,7 @@ .code-chip { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: .78rem; } .sticky-nav { position: sticky; top: .75rem; z-index: 20; backdrop-filter: blur(16px); } .mermaid { background: var(--color-base-100); border: 1px solid color-mix(in oklab, var(--color-base-content) 12%, transparent); border-radius: 1rem; padding: 1rem; overflow-x: auto; } - .decision-form fieldset { width: 100%; max-width: 100%; min-width: 0; min-inline-size: 0; margin-inline: 0; padding-inline: 0; overflow: hidden; } - .decision-form select { display: block; width: 100%; max-width: 100%; min-width: 0; text-overflow: ellipsis; } - @media print { .sticky-nav, .review-only { display: none !important; } body { background: white; } } + @media print { .sticky-nav { display: none !important; } body { background: white; } } @@ -46,7 +46,7 @@