Skip to main content

pitboard_core/
lib.rs

1//! The engine behind Pitboard: parking and restoring a person's own Claude Code and Codex
2//! logins, and reading what each has left. It serves Pitboard's own front ends, the command
3//! line and the native apps, which reach it through [`service::Pitboard`] with an explicit
4//! [`context::Context`].
5//!
6//! # What is supported
7//!
8//! This crate is published because the `pitboard` binary depends on it, not because it was
9//! designed for other programs to build on. The supported interface is [`service::Pitboard`],
10//! [`context::Context`], and the types those two return. Everything else is reachable so the
11//! front ends in this repository can reach it, and may change in any release.
12//!
13//! What a version promises, for the part that is supported: a code is a name, and names are
14//! kept. Adding an error, warning or check code is not a breaking change, which is why every
15//! enum a caller reads codes out of is `#[non_exhaustive]` and every such caller needs a
16//! fallback arm. Renaming or removing a code is a breaking change: a new minor version
17//! while Pitboard is at 0.x, as Cargo reads one, and a new major version after 1.0.
18//! A report a caller reads, such as what `uninstall` returns, may likewise say more in a
19//! later release, so those structs are `#[non_exhaustive]` too.
20//!
21//! What is deliberately not reachable: nothing outside this crate may write Pitboard's index.
22//! Every change goes through [`switch`], which records what it is about to do first and
23//! finishes an interrupted one before starting another.
24
25// Pitboard for Windows is being built, and reaches people only once the command line, the
26// app, their installers and their docs are done. Until then a Windows build of a release
27// compiles and refuses everything at run time (`release`). It compiles only for the two
28// targets its builds are made and tested on: an x64 or ARM64 PC with the MSVC toolchain.
29// A 32-bit, GNU, UWP or Arm64EC build is another program nobody has run.
30#[cfg(all(
31    windows,
32    not(all(
33        target_vendor = "pc",
34        target_env = "msvc",
35        any(target_arch = "x86_64", target_arch = "aarch64")
36    ))
37))]
38compile_error!(
39    "Pitboard for Windows is built for x86_64-pc-windows-msvc and aarch64-pc-windows-msvc \
40     alone, the targets it is built and tested on."
41);
42
43#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))]
44compile_error!(
45    "Pitboard runs on macOS, Linux and Windows. Another system needs a host of its own in \
46     `host/`, saying where its stores, processes and scheduler are."
47);
48
49pub mod api;
50pub mod app;
51pub mod assumptions;
52pub mod audit;
53pub mod autoswitch;
54pub mod budget;
55pub mod context;
56pub mod doctor;
57pub mod error;
58pub mod label;
59pub mod provider;
60pub mod redact;
61pub mod release;
62pub mod schedule;
63pub mod service;
64pub mod settings;
65pub mod state;
66pub mod status;
67pub mod statusline;
68pub mod switch;
69pub mod time;
70pub mod usage;
71pub mod words;
72
73pub(crate) mod atomic;
74pub(crate) mod fault;
75pub mod history;
76pub mod holder;
77pub(crate) mod home;
78pub mod host;
79pub(crate) mod lock;
80pub(crate) mod park;
81pub(crate) mod pending;
82pub(crate) mod proxy;
83pub(crate) mod readings;
84pub(crate) mod sessions;
85/// The program the tests start in place of a tool, reached as `testing::stand_in`. Its own
86/// tests run with the core's.
87#[cfg(any(test, feature = "test-support"))]
88#[doc(hidden)]
89pub mod stand_in;
90pub(crate) mod store;
91
92/// What the integration tests reach into: they plant and inspect parked logins in the real
93/// store, and must never touch the credential slot this machine's Claude Code reads.
94#[cfg(feature = "test-support")]
95#[doc(hidden)]
96pub mod testing {
97    pub use crate::api::scripted::{Answer, Asked, ScriptedApi, Trouble};
98    /// What a test or a fixture does to a file's access, or to make a link, which only this
99    /// system's face says how to do: the integration tests and the apps' fixtures do it here,
100    /// as the core's own tests do.
101    pub use crate::host::fs::testing as fs;
102    pub use crate::host::memory::MemoryHost;
103    pub use crate::provider::claude::paths::live_service;
104    pub use crate::provider::claude::slot::{LIVE_SERVICE, dir_hash, service_for_dir};
105    /// The program the integration tests, the model's tests and the fixtures' tests start in
106    /// place of `claude`, `codex` and every other program they run, on every system.
107    pub use crate::stand_in;
108    pub use crate::store::memory::{Fault, MemoryStore};
109    pub use crate::store::vault_read;
110    pub use crate::switch::{ScriptedSignIn, SignInScript};
111    pub use crate::time::{Clock, FixedClock};
112
113    /// Every variable Pitboard reads from its environment, by name, which a test withholds
114    /// from every command it runs unless it means to pass one on.
115    pub fn variables() -> impl Iterator<Item = &'static str> {
116        crate::context::variables()
117    }
118
119    /// Parks `contents` under `service` in this context's vault, as a change does, once the
120    /// gate every change passes has let it: a test that runs as root is refused here too.
121    pub fn vault_write(
122        ctx: &crate::context::Context,
123        service: &str,
124        contents: &str,
125    ) -> crate::error::Result<()> {
126        let permit = crate::service::gate(ctx)?;
127        Ok(crate::store::vault_write(ctx, permit, service, contents)?)
128    }
129
130    /// Deletes what is parked under `service`, once the gate has let it.
131    pub fn vault_delete(ctx: &crate::context::Context, service: &str) -> crate::error::Result<()> {
132        let permit = crate::service::gate(ctx)?;
133        Ok(crate::store::vault_delete(ctx, permit, service)?)
134    }
135
136    /// A sign-in to `which`'s tool that finished with `document`, the login the tool would
137    /// have stored, as JSON, and never ran the tool: what enrolling a second account by
138    /// signing in privately needs, in a test that may run no tool. Made once the gate has let
139    /// it, as every sign-in is.
140    pub fn signed_in(
141        ctx: &crate::context::Context,
142        which: crate::provider::ProviderId,
143        document: &str,
144    ) -> crate::error::Result<crate::switch::SignIn> {
145        let document = serde_json::from_str(document).expect("a login a test wrote as JSON");
146        crate::switch::planted(ctx, crate::service::gate(ctx)?, which, document)
147    }
148}