azure-guest-attestation-sdk
Copilot instructions3 starsChanged 4 months agoMITView on GitHub
These instructions apply to every AI agent session working on this repository, regardless of platform (Linux, Windows, macOS) or context window state. Read this file in its entirety before making any changes. AI can produce code very fast, but bad code is more expensive than ever: a codebase that is hard to change is a codebase where AI compounds entropy on every iteration. Optimize for a codebase that is easy to change, easy to test, and easy to reason about.…
Where files are headingNew Copilot instructions files have more rules about branches and pull requests, and fewer about code style.
Of the Copilot instructions files we read, the 2,336 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
The file
Show its rules and text
We split this copilot-instructions.md into the rules it gives an agent
21 rules. About 0 are common, 16 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Do not jump straight to an implementation plan or to editing files.
- Distinctive: line 21
Grill the user: ask targeted questions until you and the user share the same mental model of the change.
- Distinctive: line 24
Do not invent requirements — surface ambiguity instead.
- Distinctive: line 25
Only after the design concept is shared, propose a plan or a PRD.
- Distinctive: line 28
Prefer this over eagerly creating planning artifacts.
- Distinctive: line 46
Write or update a test that expresses the behavior (see §6).
- Very specific: line 48
Run the relevant CI command from §2 (at minimum cargo fmt, cargo clippy --workspace --all-targets -- -D warnings, and the targeted cargo …
- Very specific: line 53
If a behavior genuinely cannot be unit-tested (see §10), say so explicitly and prefer an injectorpp mock or a vtpm_tests-gated integration …
- Distinctive: line 59
Avoid sprawling shallow modules (many small files each exposing many functions).
- Distinctive: line 69
When you find related logic scattered across shallow helpers, consider consolidating it behind one interface rather than adding another …
- Distinctive: line 75
Every change is an opportunity to leave the module slightly better designed than you found it.
- Distinctive: line 83
Treat AI-generated edits as tactical: locally correct, globally unaware.
- Distinctive: line 102
Every commit must pass all of the following before push.
- Very specific: line 179
If you edit the hook, keep it in sync with §2.2 above.
- Very specific: line 290
Run cargo fmt before committing.
- Very specific: line 297
Use tracing::info! / tracing::debug!
- Distinctive: line 302
Commit messages: concise summary line, blank line, bullet-point body.
- Distinctive: line 305
Only stage files relevant to the current change.
- Distinctive: line 310
Run these in order. All must pass:
- Distinctive: line 326
If you have reference-TPM prerequisites (Perl, C toolchain):
- Distinctive: line 352
If your context window was compacted and you lost history:
≈ “Common” is an estimate: at least 1 in 5 of the 3,779 Copilot instructions files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Copilot Agent Instructions — Azure Guest Attestation SDK
These instructions apply to every AI agent session working on this repository, regardless of platform (Linux, Windows, macOS) or context window state. Read this file in its entirety before making any changes.
0. Engineering Philosophy — Software Fundamentals Still Matter
AI can produce code very fast, but bad code is more expensive than ever: a codebase that is hard to change is a codebase where AI compounds entropy on every iteration. Optimize for a codebase that is easy to change, easy to test, and easy to reason about. The rules below apply to every task.
0.1 Reach a shared design concept before writing code
Do not jump straight to an implementation plan or to editing files. When the user gives you a non-trivial task:
- Grill the user: ask targeted questions until you and the user share the same mental model of the change. Walk down each branch of the design tree and resolve dependencies between decisions one at a time. Do not invent requirements — surface ambiguity instead.
- Only after the design concept is shared, propose a plan or a PRD. For small changes, go straight to a focused diff; for larger ones, write the plan into the relevant module's README or a scratch note first.
- Prefer this over eagerly creating planning artifacts. An asset created from a misaligned design concept is worse than no asset at all.
0.2 Use the project's ubiquitous language
This repo has a precise vocabulary: Tpm, AttestationClient,
MaaProvider, ImdsClient, attest_guest, attest_platform,
submit_tee_only, vtpm_tests, NV index, TEE report, SNP, TDX, MAA, IMDS,
THIM, CoRIM, endorsement, quote, AK (attestation key), EK (endorsement
key), etc. Use these exact terms in code, comments, commit messages, and
when talking to the user. Do not invent synonyms. When introducing a new
domain concept, name it once and reuse the name everywhere.
0.3 Take small, test-driven steps — do not outrun your headlights
The rate of feedback is your speed limit. Do not produce large diffs and then check them at the end. For each logical change:
- Write or update a test that expresses the behavior (see §6).
- Make the smallest change that makes the test pass.
- Run the relevant CI command from §2 (at minimum
cargo fmt,cargo clippy --workspace --all-targets -- -D warnings, and the targetedcargo test). - Refactor if needed, then move on.
If a behavior genuinely cannot be unit-tested (see §10), say so explicitly
and prefer an injectorpp mock or a vtpm_tests-gated integration test
before falling back to "manual only".
0.4 Prefer deep modules with simple interfaces
Avoid sprawling shallow modules (many small files each exposing many functions). Prefer a smaller number of deep modules: a simple, well-considered public interface hiding meaningful implementation complexity. When touching this codebase:
- Design the interface carefully (names, types, error variants, invariants, doc comments). This is the part the rest of the system — and the AI on the next iteration — will see.
- The implementation behind that interface can be denser; the interface is what protects the rest of the codebase from churn.
- When you find related logic scattered across shallow helpers, consider consolidating it behind one interface rather than adding another helper.
- Tests should target the module's public interface, not its internals.
0.5 Invest in the design of the system every day
Every change is an opportunity to leave the module slightly better designed than you found it. Do not "just make the change" if it visibly degrades the structure (duplicates a concept, leaks an internal type, widens an interface unnecessarily, breaks a layering rule). Call out the tension to the user and propose a small refactor instead.
0.6 You are the strategic layer
Treat AI-generated edits as tactical: locally correct, globally unaware. Your job is the strategic layer — module boundaries, interfaces, the ubiquitous language, the test strategy, MSRV and cross-platform constraints (§4), and the CI contract (§2). Never delegate those.
1. Repository Overview
| Item | Value |
| Language | Rust (edition 2021) |
| MSRV | 1.90 — every change must compile on Rust 1.90 |
| Workspace members | crates/azure-tpm (TPM crate), crates/azure-guest-attestation-sdk (SDK), tools/azure-guest-attest (CLI), tools/azure-guest-attest-web (Web tool). crates/azure-tpm-testkit is excluded from the workspace (reference-TPM test harness, not published). |
| CI | .github/workflows/ci.yml — runs on both ubuntu-latest and windows-latest |
| Pre-commit hook | .githooks/pre-commit — must mirror CI flags exactly |
2. CI Checks — The Single Source of Truth
Every commit must pass all of the following before push. These are the exact commands CI runs. Do not invent variations.
Path filtering: CI uses
dorny/paths-filterto skip expensive jobs (clippy, test, vtpm-tests, msrv, docs, coverage) on PRs that only touch non-code files (markdown, scripts, etc.). Push tomainalways runs everything. Thefmtjob always runs. Code paths that trigger the full suite:crates/**,tools/**,Cargo.toml,Cargo.lock,rust-toolchain.toml,.github/workflows/ci.yml.
2.1 Format
cargo fmt --all -- --check2.2 Clippy (runs on Linux AND Windows)
cargo clippy --workspace --all-targets -- -D warningsCritical: Do NOT use
--all-features. CI does not pass--all-featuresto clippy. The reference-TPM harness (activated with--cfg vtpm_tests) pullsms-tpm-20-ref(vendored OpenSSL) which requires Perl and a C toolchain; enabling it here will fail on runners that lack those.
2.3 Unit tests — no vTPM (runs on Linux AND Windows)
cargo test --workspaceNo
--features, no--test-threads=1. Tests must be safe to run in parallel across threads.
2.4 vTPM integration tests (runs on Linux AND Windows)
RUSTFLAGS="--cfg vtpm_tests" cargo nextest run -p azure-guest-attestation-sdkThe reference-TPM harness is activated by the custom
--cfg vtpm_testsflag (NOT a Cargo feature). It is pulled via cfg-gated dev-dependencies so the published crates never depend onms-tpm-20-ref.
2.5 MSRV check (Linux only)
cargo +1.90 check --workspaceDev-dependencies are excluded from
cargo check, so newer crates likeinjectorppare fine as long as the main code compiles on 1.90.
2.6 Docs
RUSTDOCFLAGS=-Dwarnings cargo doc --workspace --no-deps2.7 Coverage (Linux only)
RUSTFLAGS="--cfg vtpm_tests" cargo llvm-cov --workspace \
--ignore-filename-regex '(src/lib\.rs|azure-guest-attest/src/main\.rs)$' \
--fail-under-lines 603. Pre-Commit Hook
The hook at .githooks/pre-commit must mirror the CI clippy flags:
cargo clippy --workspace --all-targets -- -D warningsIf you edit the hook, keep it in sync with §2.2 above. Never add
--all-features to the hook.
4. Cross-Platform Rules
CI tests on both Linux and Windows. Every change must work on both.
| Rule | Details |
| No Unix-only paths | Use std::path / std::env::consts::OS — never hardcode /dev/... outside #[cfg(unix)] |
| No Unix-only APIs | io::Error::other() requires Rust ≥ 1.74 — OK for MSRV 1.90 |
Windows #[cfg] | Windows-specific code must be gated with #[cfg(target_os = "windows")] |
| Line endings | The repo uses LF (.gitattributes). Don't introduce CRLF. |
| Dev-dependencies | Must compile on all CI platforms (Linux + Windows). If a dev-dep is platform-specific, gate its usage with #[cfg(...)] |
5. Reference-TPM Harness (--cfg vtpm_tests)
The in-process reference TPM is not a Cargo feature — it is activated by a
custom --cfg vtpm_tests compile flag, set via RUSTFLAGS. This keeps the
heavy git-sourced ms-tpm-20-ref (vendored OpenSSL, needs Perl + a C
toolchain) out of the default build graph and out of the published crates
(git dependencies cannot be published to crates.io).
| Config | Purpose | How |
--cfg vtpm_tests | Reference-TPM tests | azure-tpm uses a cfg-gated dev-dependency on ms-tpm-20-ref + an in-crate harness (Tpm::open_reference_for_tests()). The SDK uses the non-published azure-tpm-testkit crate (azure_tpm_testkit::reference_tpm()). Gate test code with #[cfg(vtpm_tests)]. |
| (none / default) | Normal SDK functionality | CI clippy + unit tests run without the flag. |
Why not a Cargo feature? Features cannot gate dev-dependencies, and a git dependency (even optional/feature-gated) blocks
cargo publish. A--cfgtoggling a[target.'cfg(vtpm_tests)'.dev-dependencies]table is the only mechanism that keeps the heavy dep out of the default graph, out of the published manifest, AND activates it on demand.
azure-tpm-testkitis deliberately excluded from the workspace (see rootCargo.tomlexclude) socargo build/clippy/test --workspacenever pullsms-tpm-20-ref. It is compiled only as a cfg-gated dev-dependency of the SDK. Theunexpected_cfgslint is satisfied by the[workspace.lints.rust]check-cfgdeclaration (opt in per-crate with[lints] workspace = true).
Key invariant:
cargo clippy --workspace --all-targetsandcargo test --workspace(without the flag) must succeed and must NOT compilems-tpm-20-ref. All test code that needs the reference TPM must be gated with#[cfg(vtpm_tests)].
6. Testing Conventions
6.1 Test naming
- Pure unit tests: descriptive name (
parse_version_pair_major_minor) - vTPM integration tests: prefix
vtpm_and gate with#[cfg(vtpm_tests)] - Injectorpp (mocked) tests: no special prefix; use
unsafe { ... }blocks for_uncheckedAPI
6.2 Injectorpp (runtime mocking)
The SDK uses injectorpp (dev-dependency) for runtime function mocking.
Required workspace settings (already in root Cargo.toml):
[profile.test]
opt-level = 0
debug = true
lto = false
codegen-units = 1
incremental = falseAPI pattern (use the unchecked API for methods with complex signatures):
fn fake_some_method(_self: &SomeStruct, _arg: &str) -> io::Result<Vec<u8>> {
Ok(b"mocked".to_vec())
}
#[test]
fn test_with_mock() {
use injectorpp::interface::injector::*;
let mut injector = InjectorPP::new();
unsafe {
injector
.when_called_unchecked(injectorpp::func_unchecked!(SomeStruct::method))
.will_execute_raw_unchecked(injectorpp::func_unchecked!(fake_some_method));
}
// ... test code that exercises the mocked path ...
}Rules:
- Fake functions must be declared as standalone
fnitems (not closures) with the exact same signature as the target method (including&selfas first param). - The injector variable must stay alive (not dropped) for the duration of the test — dropping it restores the original function.
- injectorpp patches process-global memory, so tests that mock the same
function must not run in parallel.
cargo testmay interleave them;cargo nextestgives each test its own process. - injectorpp supports Linux, macOS, and Windows.
6.3 Thread safety
CI runs cargo test --workspace without --test-threads=1. Tests must be
safe to run in parallel unless they use injectorpp to mock the same
function, in which case the mocking scope should be as narrow as possible.
6.4 NV index ranges
vTPM tests that write NV indices must use distinct index values to avoid
collisions when sharing a reference TPM instance (e.g. 0x0150_XXXX).
7. Code Style
- Run
cargo fmtbefore committing. - License header on every
.rsfile:// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. - Rustdoc comments on all public items.
- Use
tracing::info!/tracing::debug!(notprintln!) for diagnostics.
8. Git Workflow
- Each task gets its own branch (e.g.
snp_fix,injector,test_coverage). - Commit messages: concise summary line, blank line, bullet-point body.
- The pre-commit hook runs fmt + clippy automatically. If the hook fails, fix the issues before committing — do not bypass it.
- Only stage files relevant to the current change. Do not stage unrelated files, coverage artifacts, embedded git repos, etc.
9. Verifying Before Push — Checklist
Run these in order. All must pass:
# 1. Format
cargo fmt --all -- --check
# 2. Clippy (matches CI exactly)
cargo clippy --workspace --all-targets -- -D warnings
# 3. Unit tests (no features, parallel)
cargo test --workspace
# 4. MSRV (if Rust 1.90 toolchain is installed)
cargo +1.90 check --workspaceIf you have reference-TPM prerequisites (Perl, C toolchain):
# 5. vTPM tests
RUSTFLAGS="--cfg vtpm_tests" cargo nextest run -p azure-guest-attestation-sdk10. What Cannot Be Unit-Tested
Some code paths require real hardware or network and cannot be covered by unit tests or injectorpp mocking:
| Category | Reason |
Tpm::open() | Needs /dev/tpmrm0 or Windows TBS |
AttestationClient::attest_guest() / attest_platform() | Needs Tpm instance (private inner) |
ImdsClient HTTP body parsing | reqwest::Response has no public constructor |
MaaProvider::attest_guest() / submit_tee_only() | Creates reqwest::Client internally |
Windows #[cfg] blocks on Linux CI | Structurally unreachable |
These are covered by:
- the
--cfg vtpm_testsreference-TPM harness - Manual testing on real CVM hardware
- Integration test environments
11. Resuming After Context Compaction
If your context window was compacted and you lost history:
- Read this file first — it contains all the rules.
- Read
.github/workflows/ci.yml— it is the ground truth for CI. - Read
.githooks/pre-commit— must match CI clippy flags. - Check
git log --oneline -20— understand recent changes. - Check
git branch— know which branch you're on. - Never guess CI flags — look them up from ci.yml every time.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

