Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: DG-0 contracts
name: Contracts and service boundary

on:
push:
Expand All @@ -24,25 +24,29 @@ jobs:
persist-credentials: false
- name: Select the qualification toolchain
run: rustup toolchain install 1.95.0 --profile minimal --component clippy --component rustfmt
- name: DG-0 qualification (fake backend only)
- name: Contract regression and workspace validation
run: python3 scripts/validate.py --output target/qualification/ci
- name: Canonical authority functional checks
run: python3 scripts/qualify.py dg1-authority --output target/qualification/dg1-authority
- name: Native local authentication functional checks
run: python3 scripts/qualify.py dg1-auth --output target/qualification/dg1-auth
- name: Preserve qualification evidence
if: always()
uses: actions/upload-artifact@v6
with:
name: dg0-${{ matrix.os }}-${{ github.run_attempt }}
path: target/qualification/ci/
path: target/qualification/
if-no-files-found: error
- name: Summarize qualification
if: always()
run: |
python3 - <<'PY'
import json, os
from pathlib import Path
path = Path('target/qualification/ci/report.json')
if path.exists():
report = json.loads(path.read_text())
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as out:
out.write('DG-0: ' + report['status'] + '\n\n')
out.write('Fake-backend contracts only. Runtime/OS/SLO qualification is not run.\n')
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as out:
for name in ['ci', 'dg1-authority', 'dg1-auth']:
path = Path('target/qualification') / name / 'report.json'
status = json.loads(path.read_text())['status'] if path.exists() else 'not_run'
out.write(name + ': ' + status + '\n\n')
out.write('Contract regression and service/UDS functional checks only. Workload launch, OS resource control, self-use and foreground SLO qualification are not run.\n')
PY
110 changes: 109 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["crates/contract", "crates/core"]
members = ["crates/contract", "crates/core", "crates/daemon", "crates/client"]

[workspace.package]
version = "0.1.0"
Expand All @@ -18,6 +18,7 @@ rusqlite = { version = "0.32", features = ["bundled"] }
tempfile = "3.27"
uuid = { version = "1.20", features = ["v4"] }
libc = "0.2"
toml = "=0.9.12"

[profile.dev]
debug = 1
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,39 @@

DevGuard centralizes resource admission for development workloads while preserving the resources needed to inspect and stop them.

The repository currently implements **DG-0: contracts and a durable authority core**. It does not yet install a daemon, launch user commands, apply macOS policies, or enforce Linux cgroups. The CLI examples in the approved design describe DG-1 and later work.
The repository implements **DG-0 contracts and a durable authority core**, with DG-1 now in progress. C01 supplies canonical paths/bootstrap/storage checks; C02 supplies authenticated, bounded local communication with a small client and private credential-FD transfer. PR and post-merge main delivery evidence is tracked separately from implementation. Native registration, principals, leases and execution remain closed until the required native evidence and launch work in P2/P3; macOS resource policies and Linux cgroups are not yet available. Use the operating guide for actual command availability; the design also contains future interfaces.

- [Authoritative design reference](docs/design.md) · [Korean translation](docs/ko/design.md)
- [Historical approved design (Korean, immutable)](docs/design.ko.md)
- [Implemented contracts and trust boundaries](docs/contracts.md)
- [Service boundary operations](docs/operations.md)
- [Milestones and the CodeSpace dependency path](docs/milestones.md)
- [Detailed execution plans, adoption gates and PR delivery](docs/planning/README.md)
- [Machine-readable milestone state](milestones.json)

## Validate DG-0
## Validate the current implementation

Use Rust **1.95.0**, including rustfmt and Clippy, and Python 3.11 or newer. A rustup installation honors `rust-toolchain.toml`. A standalone toolchain can be placed first in `PATH`. The validator checks the compiler it actually executes.

```sh
python3 scripts/validate.py --offline
python3 scripts/qualify.py dg1-authority --offline
python3 scripts/qualify.py dg1-auth --offline
```

Omit `--offline` when the locked crates have not been downloaded. Builds and tests use one Cargo job and one test thread by default. Results, source fingerprints and logs are written under `target/qualification/`. A newer compiler can be used with `--allow-toolchain-mismatch` for a supplemental check, which never counts as qualification for 1.95.0.

DG-0 tests use an explicitly fake OS backend. A passing report establishes the tested accounting, persistence and state-transition contracts. macOS launch, Linux enforcement, browser responsiveness and self-governed execution remain `not_run`.
DG-0 tests use an explicitly fake OS backend. Their passing reports establish accounting, persistence and state-transition contracts. The additional C01/C02 suites check actual local storage, peer observations and credential transport within bounded fixtures. They do not qualify native registration or launch, Linux enforcement, browser responsiveness or self-governed execution; these remain `not_run`.

## Repository boundaries

`devguard-contract` provides transport-independent resource types, execution identities, evidence and compatibility requirements. `devguard-core` provides static accounting, authenticated consumer registration against trusted peer observations, durable admission, fenced launch transitions, pressure policy and evidence-based reconciliation. It starts no processes and contains no CodeSpace or Codex dependency.

The approved local checkout is `/Volumes/DevData/Projects/IdeaProjects/DevGuard`. Existing `.codex` settings are preserved locally and ignored by Git. Build output, journals, qualification evidence and local toolchains are also ignored. The approved design is preserved byte-for-byte; its checksum is recorded in `docs/design-source.json`.

The public source repository is [novelKR/DevGuard](https://github.com/novelKR/DevGuard). CodeSpace runtime consumption begins at CS-RG after DG-1 qualification. The foundation does not publish crates or install a running host service.
`devguard-daemon` provides the canonical configuration/storage boundary and foreground `devguardd serve`. `devguard-client` supplies versioned UDS communication and private credential handoff without depending on the authority core. Successful authentication is not an instance registration or a workload lease, and does not isolate malicious processes sharing the operating UID.

The public source repository is [novelKR/DevGuard](https://github.com/novelKR/DevGuard). CodeSpace runtime consumption begins at CS-RG after DG-1 qualification. Crates are not published, and no installer or LaunchAgent is available yet.

The detailed plan defines 46 proposed implementation commit units in 23 logical PR groups. It records single registration by the execution-owning Runner and opt-in Gateway restart recovery while an independent Runner remains alive. Planning completion does not change runtime milestone status. See the [consumer readiness gates](docs/planning/consumer-readiness.md), [CodeSpace mapping](docs/planning/codespace-integration.md), and [verification and evidence rules](docs/planning/verification.md).

Expand Down
18 changes: 18 additions & 0 deletions crates/client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "devguard-client"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
publish.workspace = true
license.workspace = true
repository.workspace = true
description = "Small authenticated local DevGuard protocol client"

[dependencies]
devguard-contract = { path = "../contract" }
serde.workspace = true
serde_json.workspace = true
libc.workspace = true

[dev-dependencies]
tempfile.workspace = true
46 changes: 46 additions & 0 deletions crates/client/examples/inspect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Bounded foreground inspection example; no workload registration or execution.
use devguard_client::{credential::take_inherited, protocol::CallerCredential, Client};
use devguard_contract::{Compatibility, Error, ErrorCode, Result, PROTOCOL_VERSION};
use std::collections::BTreeSet;
use std::path::Path;

fn invalid() -> Error {
Error::new(
ErrorCode::InvalidRequest,
"usage: inspect SOCKET UID CONSUMER GENERATION CREDENTIAL_FD",
)
}
fn run() -> Result<()> {
let args: Vec<_> = std::env::args().skip(1).collect();
if args.len() != 5 {
return Err(invalid());
}
let uid = args[1].parse().map_err(|_| invalid())?;
let fd = args[4].parse().map_err(|_| invalid())?;
// SAFETY: this dedicated executable takes ownership of the descriptor passed
// by its caller, before creating any clients or descriptors of its own.
let secret = unsafe { take_inherited(fd) }?;
let mut client = Client::connect(
Path::new(&args[0]),
uid,
Compatibility {
minimum_protocol: PROTOCOL_VERSION,
maximum_protocol: PROTOCOL_VERSION,
required: BTreeSet::new(),
},
)?;
client.authenticate(CallerCredential::Consumer {
consumer_id: args[2].clone(),
generation: args[3].clone(),
secret,
})?;
let status = client.status()?;
println!("{}", serde_json::to_string(&serde_json::json!({"peer":client.hello.authority,"caller":client.hello.caller,"status":status})).map_err(|_| invalid())?);
Ok(())
}
fn main() {
if let Err(error) = run() {
eprintln!("{error}");
std::process::exit(1);
}
}
Loading