Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Workspace and crates

Note

Project policy: draft. This page is the only home of Shiin’s crate list. The workspace has twelve product crates and xtask, including partial approval, evidence, and integration implementations. Host, platform, and milestone validation remain incomplete.

Shiin is built as a single Cargo workspace. This page describes the repository layout, the crates in the workspace, what each crate is responsible for, and the rules that keep the crate graph small, acyclic, and honest about what exists. The decision behind the layout is ADR-0035.

What exists today

PathStatePurpose
Cargo.tomlexistsVirtual workspace manifest with shared package metadata, [workspace.dependencies], and [workspace.lints]
rust-toolchain.tomlexistsPinned toolchain; see Toolchain and MSRV
rustfmt.toml, .editorconfigexistFormatting rules; see Coding standards
.cargo/config.tomlexistsDefines the cargo xtask alias
xtask/existsRepository tooling; today it provides cargo xtask docs-check
crates/existsProduct crates, each created in the milestone that needs it
fuzz/existsWire-request and classification fuzz targets in a separate nightly workspace

The workspace section of today’s Cargo.toml is:

[workspace]
resolver = "3"
members = [
  "crates/shiin-approval",
  "crates/shiin-audit",
  "crates/shiin-classify",
  "crates/shiin-client",
  "crates/shiin-cli",
  "crates/shiin-engine",
  "crates/shiin-hooks",
  "crates/shiin-ipc",
  "crates/shiin-mcp-proxy",
  "crates/shiin-policy",
  "crates/shiin-schema",
  "crates/shiind",
  "xtask",
]

The alias in .cargo/config.toml is:

[alias]
xtask = "run --quiet --package xtask --"

Layout

Cargo.toml             virtual workspace manifest (no root package)
rust-toolchain.toml    pinned toolchain
rustfmt.toml           formatter settings
.editorconfig          editor settings
.cargo/config.toml     cargo aliases, including `cargo xtask`
crates/<name>/         twelve product crates with milestone validation in progress
xtask/                 repository tooling, never published
fuzz/                  fuzz targets, a separate nightly workspace
docs/                  this documentation
  • The root Cargo.toml is a virtual manifest. It has no [package] section, so no crate is privileged by living at the root.
  • Product crates live in crates/<name>/, and the directory name is the crate name.
  • xtask/ follows the cargo xtask convention for repository automation written in Rust. It sets publish = false. Product crates never depend on it.
  • The fuzz/ directory has its own [workspace] table and lockfile. Its nightly-only dependencies never enter the main workspace, its lockfile, or its MSRV check. See Testing strategy.
  • Every member inherits edition, rust-version, license, repository, and authors from [workspace.package].
  • Every member sets [lints] workspace = true, except shiin-platform, which declares its own table as described in Coding standards.

Crates

The table lists every product crate in the workspace, its responsibility, and the milestone that owns its implementation and validation.

CrateResponsibilityMilestone
shiin-schemaWire types for action requests, decisions, approval messages, and audit events, matching the JSON Schemas in docs/spec/schemas/v0.1
shiin-classifyNormalizers for paths, shell commands, URLs, and experimental SQL, and the derivation of effects from themv0.1 (SQL: v0.5; host integration open)
shiin-policyThe TOML front-end and its compilation to Cedar, policy layering, and diagnosticsv0.1
shiin-engineThe pure, synchronous evaluation pipelinev0.1
shiin-auditThe hash-chained audit log, verification, replay, and receipts behind a featurev0.1 (receipts: v0.4; external review open)
shiin-hooksHost hook adapters: a claude_code module, then codex and cursor modulesv0.1 (Codex and Cursor: v0.2)
shiin-cliThe shiin binary: init, check, explain, policy validate, policy test, hook, task, pending, approve, log, verify, replay, and doctorv0.1
shiin-approvalThe approval state machine and grantsv0.3 (source; review open)
shiin-ipcLocal transport over Unix domain sockets and Windows named pipesv0.3 (Unix source; Windows validation open)
shiindThe per-user daemonv0.3 (Unix source; platform validation open)
shiin-clientThe Rust client for daemon evaluation and policy validationv0.5 (early source; conformance open)
shiin-mcp-proxyThe MCP proxy with upstream stdio and downstream stdio or Streamable HTTPv0.5 (HTTP: v0.6; external conformance open)
shiin-platformThe only crate allowed to contain unsafe code; created only if neededas needed
xtaskRepository toolingexists
fuzz/Fuzz targets; a separate workspace, not a member cratev0.1

shiin-cli gains its subcommands milestone by milestone; the CLI reference says which command arrives when.

The engine crates are shiin-schema, shiin-classify, shiin-policy, and shiin-engine. Several rules on this page and on Async and concurrency apply to that group.

Rules

Never pre-create crates

  • A crate is created in the pull request that adds its first real functionality, in the milestone the table names.
  • Empty, placeholder, or “coming soon” crates are not created, in the repository or on crates.io. Crate names are not reserved on crates.io in advance.
  • A pull request that adds, splits, merges, or renames a crate updates the table on this page in the same change.
  • A change that alters the dependency direction below, or adds a crate to the set of stable public APIs, needs an ADR.

Stable and internal APIs

  • shiin-schema and shiin-client are the only crates intended to become stable public APIs. Other projects may depend on them, and after 1.0 they follow the guarantees in Versioning and stability.
  • Every other product crate is published to crates.io, because a published crate can only depend on other published crates, but is documented as internal before 1.0. Its crate-level documentation starts with a sentence saying that the API is internal to Shiin and can change in any release.
  • xtask is never published.

Dependencies are declared once, with default features off

  • Every third-party dependency is declared in [workspace.dependencies] with a version requirement and default-features = false.
  • Members reference it with workspace = true and enable only the features they use.
  • Default features have to be disabled at the workspace level: a member can add features to a workspace dependency but cannot remove default features that the workspace entry leaves enabled.
  • Admitting a dependency at all is governed by Dependencies and supply chain.

Today’s Cargo.toml does not fully follow this rule yet. jsonschema sets default-features = false, but the regex, serde_json, and walkdir entries do not. Several product dependencies also retain default features. Reviewing and disabling unnecessary defaults remains open supply-chain work.

No networking in the engine crates

  • The engine crates never open network connections and never depend on crates that do, such as HTTP clients, TLS stacks, or async runtimes.
  • shiin-engine performs no I/O of any kind (ADR-0010).
  • shiin-classify and shiin-policy may read the local file system where their job requires it, such as resolving a path or loading a policy file.
  • cargo xtask dependency-check inspects the normal (non-dev) dependency tree of each engine crate and fails if a known network stack or async runtime appears. The documentation CI job runs this check; the prohibited-crate list still needs review when new dependencies are admitted.

shiin-engine builds for WebAssembly as a purity guard

From v0.4, CI builds shiin-engine for the wasm32-unknown-unknown target on every pull request. The isolated check disables the schema crate’s default runtime feature through its workspace dependency. OS clocks and randomness remain explicitly enabled by adapters and services; wire types and timestamp formatting do not require them.

  • The build fails if the engine, or anything it depends on, needs operating-system facilities that target lacks, such as native system libraries, platform-specific system calls, or an operating-system randomness source. That makes it a cheap, continuous guard against I/O and platform dependencies creeping into the engine.
  • The guard is not complete. The standard library still compiles calls such as file and socket operations for that target and only fails at run time, so the build does not catch them. Planned disallowed_methods and disallowed_types Clippy configuration for the engine crates covers that gap.
  • The guard starts at v0.4, when replay makes the engine’s determinism a guarantee users rely on.
  • Building for this target is a test, not a support promise. Supported platforms are listed in Platform support.

Dependency direction

Dependencies point from binaries, through adapters and services, down to the engine crates, and never back up. shiin-platform, if it is ever created, sits below everything that needs it and depends on no other Shiin crate.

flowchart TD
    subgraph binaries["Binaries"]
        cli["shiin-cli<br/>(shiin)"]
        daemon["shiind"]
        proxy["shiin-mcp-proxy"]
    end
    subgraph services["Adapters, approvals, evidence, and transport"]
        hooks["shiin-hooks"]
        approval["shiin-approval"]
        audit["shiin-audit"]
        client["shiin-client"]
        ipc["shiin-ipc"]
    end
    subgraph enginecrates["Engine crates: no networking"]
        engine["shiin-engine"]
        policy["shiin-policy"]
        classify["shiin-classify"]
        schema["shiin-schema"]
    end
    platform["shiin-platform<br/>(only if needed)"]

    binaries --> services
    binaries --> enginecrates
    services --> enginecrates
    client --> ipc
    engine --> policy
    engine --> schema
    policy --> schema
    classify --> schema
    binaries -.-> platform
    services -.-> platform
    classify -.-> platform

The diagram is enforced by these rules:

  1. A crate never depends on a crate in a group above it, and libraries never depend on binaries.
  2. The engine crates depend only on each other and on third-party crates that satisfy the no-networking rule.
  3. shiin-engine does not depend on shiin-classify. Adapters classify what a host reports into an action request; the engine only evaluates action requests.
  4. shiin-client depends only on shiin-schema, shiin-ipc, and third-party crates, so embedding the public client library never pulls in Cedar or the engine.
  5. shiin-platform depends on no other Shiin crate. shiin-engine and shiin-schema never depend on it.
  6. xtask may depend on product crates, for example to validate examples against real types; no product crate depends on xtask.

Dependencies within a group are allowed when they respect these rules; Cargo rejects cycles.

Adding a crate

  1. Confirm the crate is in the table for the current milestone, or update the table in the same pull request.
  2. Create crates/<name>/Cargo.toml inheriting the [workspace.package] fields and [lints] workspace = true.
  3. Write crate-level documentation stating the crate’s responsibility and, before 1.0, that its API is internal (unless it is shiin-schema or shiin-client).
  4. Admit each new dependency under Dependencies and supply chain.
  5. If the crate is security-sensitive, add it to Security-sensitive changes.