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
11 changes: 7 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,18 @@ jobs:

# `--all-targets` so tests and examples are linted too; `-p`-scoped or
# target-less clippy silently skips test code.
- name: Clippy (default features — the no_std `ui` build)
# Default features are now empty (`auth` is opt-in), so this lints the
# bare crate; the all-features run below is the one that covers auth.
- name: Clippy (default features)
run: cargo clippy --all-targets -- -D warnings

- name: Clippy (all features)
run: cargo clippy --all-targets --all-features -- -D warnings

# Run BOTH feature sets: the `ui` tests are the only ones in the default
# build, and the `auth` tests only exist behind the feature — running one
# configuration would report a green suite that never touched half the crate.
# Run BOTH feature sets. Every test lives behind `auth`, so the default
# run reports "0 passed; ok" — which is exactly why the all-features run
# below has to exist: a single default-only run would be a green suite
# that executed nothing.
- name: Test (default features)
run: cargo test

Expand Down
105 changes: 14 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,27 @@
<img src="https://img.shields.io/badge/OAuth_PKCE_·_localhost_callback-00A6A6?style=flat-square" alt="OAuth PKCE localhost callback">
<img src="https://img.shields.io/badge/M2M_client__credentials-00A6A6?style=flat-square" alt="M2M client credentials">
<img src="https://img.shields.io/badge/0600_credential_store-F49F0A?style=flat-square" alt="0600 credential store">
<img src="https://img.shields.io/badge/design_tokens_·_no__std-FF6B6C?style=flat-square" alt="design tokens no_std">
<img src="https://img.shields.io/badge/PKCE_·_M2M_·_0600_store-FF6B6C?style=flat-square" alt="PKCE M2M 0600 store">
</p>

<p align="center">
<a href="#what-is-this"><b>What it is</b></a> &nbsp;·&nbsp; <a href="#feature-tour"><b>Feature tour</b></a> &nbsp;·&nbsp; <a href="#quickstart"><b>Quickstart</b></a> &nbsp;·&nbsp; <a href="#honest-status"><b>Honest status</b></a> &nbsp;·&nbsp; <a href="#migrating-from-smooai-ui"><b>Migrating</b></a> &nbsp;·&nbsp; <a href="#-part-of-smoo-ai"><b>Platform</b></a>
<a href="#what-is-this"><b>What it is</b></a> &nbsp;·&nbsp; <a href="#feature-tour"><b>Feature tour</b></a> &nbsp;·&nbsp; <a href="#quickstart"><b>Quickstart</b></a> &nbsp;·&nbsp; <a href="#honest-status"><b>Honest status</b></a> &nbsp;·&nbsp; <a href="#-part-of-smoo-ai"><b>Platform</b></a>
</p>

---

> **Every Smoo AI Rust client needs the same three things — so they live in one crate.** Design tokens + the smoo monogram (`ui`), the full Supabase auth story — browser OAuth with PKCE, email+password, session refresh, M2M `client_credentials` — with a shared 0600 on-disk credential store (`auth`), One Rust crate, feature-gated so the bare `ui` build stays `no_std` with zero dependencies. Consumed in production by the [`th` CLI](https://github.com/SmooAI/smooth). **Rust-only today; not yet on crates.io** — npm / NuGet / PyPI siblings are planned, not built.
> **One Supabase auth story, shared by every Smoo AI Rust client.** Browser OAuth with PKCE and a localhost callback, email+password for headless environments, refresh-token rotation, the M2M `client_credentials` grant — and one `0600` on-disk credential store holding user and machine sessions side by side. Consumed in production by the [`th` CLI](https://github.com/SmooAI/smooth). **Rust-only today; not yet on crates.io** — a git dependency is the install path.

## What is this?

A Smoo AI Rust client (smooblue, observability-studio, `th`, `smoo admin`, …) typically needs the same three things:

1. **Design tokens + monogram** — so the UI looks like Smoo AI.
2. **Auth** — Supabase user OAuth (browser login), email+password, session refresh, AND M2M `client_credentials` grant (service accounts), with one shared on-disk `CredentialsStore`.
3. **LLM access** — exchanging a user session JWT for an org-scoped `llm.smoo.ai` bearer. *(Not built — there is deliberately no feature flag for it yet; see [Honest status](#honest-status).)*

Each of these has been re-implemented in every consumer at least once. This crate makes them one dependency. It absorbs the standalone [`SmooAI/ui`](https://github.com/SmooAI/ui) crate: `ui` is one module alongside `auth` — same constants, same paths. This repo's `shared/` is the **source of truth** for the design system; SmooAI/ui carries a copy, and a CI gate there fails if the two ever diverge.

```mermaid
%%{init: {'theme':'base','themeVariables':{
'background':'#020618','primaryColor':'#0b1426','primaryTextColor':'#e6edf6','primaryBorderColor':'#2b3a52',
'lineColor':'#7c8aa0','secondaryColor':'#0b1426','tertiaryColor':'#0b1426','fontFamily':'ui-sans-serif, system-ui, sans-serif',
'clusterBkg':'#0b1426','clusterBorder':'#22304a'}}}%%
flowchart LR
subgraph CRATE["smooai-client-shared"]
UI["ui (default)<br/>STYLES · MONOGRAM_SVG · tokens::*<br/>zero deps · no_std"]
AUTH["auth (feature)<br/>oauth · password · refresh · m2m<br/>CredentialsStore (0600)"]
end
AUTH -->|"PKCE localhost callback"| SB[("Supabase<br/>/auth/v1")]
AUTH -->|"client_credentials"| TOK[("auth.smoo.ai/token")]
TH["th CLI<br/>(git dep, features=[auth])"] --> AUTH
APPS["smooblue · desktop apps"] -.->|"can consume"| UI

classDef warm fill:#f49f0a,stroke:#ff6b6c,color:#1a0f00;
classDef teal fill:#00a6a6,stroke:#00c2c2,color:#011;
class AUTH warm
class UI,TH teal
```
The auth plumbing every Smoo AI Rust client needs identically, in one crate instead of re-implemented per app:

---
1. **Sign a human in** — browser OAuth (PKCE + localhost callback), or email+password where no browser exists.
2. **Keep them signed in** — refresh-token grant with Supabase's rotation handled, plus a renew-ahead window.
3. **Sign a machine in** — the RFC 6749 `client_credentials` grant against `auth.smoo.ai/token`.
4. **Put the credentials somewhere sane** — a `0600` store holding user and M2M sessions together.

> **This crate used to carry the design system too**, and called itself [`SmooAI/ui`](https://github.com/SmooAI/ui)'s successor. That migration was never finished: nothing ever imported `smooai_client_shared::ui`, while the real design-system consumers (smooblue, observability-studio) depended on `SmooAI/ui` directly. Two copies of the same files is a drift surface, and it had already drifted — this crate spent weeks serving a monogram missing its inner 'S'. The copy is gone. **`SmooAI/ui` owns the design system**; this crate is auth.

## Feature tour

Expand All @@ -65,7 +43,6 @@ flowchart LR
| ♻️ | [**Session refresh**](#-session-refresh) | `refresh_token` grant with rotation handling + a renew-ahead window |
| 🤖 | [**M2M `client_credentials`**](#-m2m-client_credentials) | RFC 6749 service-account grant against `auth.smoo.ai/token` |
| 💾 | [**`CredentialsStore`**](#-the-credentials-store) | One 0600 on-disk store for user + M2M sessions, side by side |
| 🎨 | [**Design tokens**](#-design-tokens-ui) | The `smooai-ui` surface, verbatim, as the `ui` module |

All snippets below are the actual API, verified against `rust/src/`.

Expand Down Expand Up @@ -134,18 +111,6 @@ if let Some(creds) = store.load()? {
}
```

### 🎨 Design tokens (`ui`)

The full `smooai-ui` surface, lifted verbatim — canonical OKLCH stylesheet, monogram, and token constants for non-DOM code paths. Zero dependencies, `no_std` when built with only the default `ui` feature:

```rust
use smooai_client_shared::ui::{STYLES, MONOGRAM_SVG, tokens};

let accent = tokens::SMOOAI_GREEN; // "oklch(0.657 0.112 194.8)"
```

---

## Quickstart

**`smooai-client-shared` is not published to crates.io.** Consume it as a git dependency — this is exactly how the [`th` CLI](https://github.com/SmooAI/smooth) consumes it in production (rev-pinned, `features = ["auth"]`):
Expand All @@ -159,78 +124,36 @@ smooai-client-shared = { git = "https://github.com/SmooAI/client-shared.git", fe

| Feature | Adds | Pulls | Status |
| --- | --- | --- | --- |
| `ui` (default) | `STYLES`, `MONOGRAM_SVG`, `tokens::*` | nothing — `no_std` | ✅ working |
| `auth` | Supabase OAuth + password + refresh, M2M, `CredentialsStore` | `tokio`, `reqwest`, `axum`, `serde`, … | ✅ working, 28 unit tests |

An `llm` feature (JWT → `llm.smoo.ai` org-session exchange, pearl th-f7b20f) is **planned and deliberately absent**. It previously existed as a flag over a six-line doc-comment module: `--features llm` compiled and produced nothing, which is worse than an honest gap. It returns when there is code behind it.

Run the tests yourself — 34 unit tests across `ui` + `auth` (OAuth callback/PKCE, token rotation, store round-trips, permission bits, token/CSS drift):

```bash
cd rust && cargo test --all-features
```

CI (`.github/workflows/rust.yml`) runs `cargo fmt --check`, `clippy --all-targets -D warnings` and the test suite in **both** feature configurations — the default `no_std` `ui` build and `--all-features` — plus a module-tree check that fails if any `.rs` file is unreachable from a `mod` declaration.

---
`auth` is not a default. It is the crate's only surface and its one consumer always asks for it, but the tree it pulls in is heavy enough to be explicit about.

## Honest status

| Surface | Status |
| --- | --- |
| **Rust `ui`** | ✅ Working — the `tokens` constants are **generated** from `shared/tokens.json` at build time, and cross-checked against `shared/styles.css` in both directions (every token matches its custom property; every colour the CSS declares has a token) |
| **Rust `auth`** | ✅ Working — OAuth PKCE localhost-callback (387 LOC), password grant, refresh with rotation, M2M, 0600 `CredentialsStore`; 28 unit tests; consumed by the `th` CLI in production |
| **Rust `llm`** | ❌ Not built — no module, no feature flag. Pearl th-f7b20f tracks it |
| **crates.io** | ❌ Not published — git dependency is the only install path |
| **npm / NuGet / PyPI** | 📦 Planned, no code — the `src/`, `dotnet/`, `python/` directories in the layout below don't exist yet |
| **npm / NuGet / PyPI** | 📦 Planned, no code — there is no `src/`, `dotnet/` or `python/` directory |

## Layout

```
client-shared/
├── shared/ # cross-language source of truth
│ ├── styles.css # OKLCH tokens + base component CSS
│ ├── monogram.svg # smoo monogram
│ ├── tokens.json # THE token source — the Rust `tokens` module is generated from it
│ ├── tokens_codegen.rs # generator, run from rust/build.rs
│ └── tokens_css_check.rs # asserts tokens.json <-> styles.css agree, both ways
└── rust/ # smooai-client-shared (git dependency; crates.io planned)
├── Cargo.toml
└── src/
├── lib.rs
├── ui/ # lifted verbatim from smooai-ui
└── auth/ # oauth · password · refresh · m2m · storage (feature = "auth")
```

npm (`src/`), NuGet (`dotnet/`), and PyPI (`python/`) packages are roadmap, not directories.

## Migrating from `smooai-ui`

Both crates are git dependencies (neither is on crates.io — there is no published shim). Migration is a source-level swap:

```toml
# before
smooai-ui = { git = "https://github.com/SmooAI/ui.git", branch = "main" }

# after — default features include "ui", same zero-dep no_std tree
smooai-client-shared = { git = "https://github.com/SmooAI/client-shared.git" }
```

```rust
// before
use smooai_ui::{STYLES, MONOGRAM_SVG, tokens};

// after — every const and sub-module at the same relative path under ui::
use smooai_client_shared::ui::{STYLES, MONOGRAM_SVG, tokens};
```

The `ui` module is API-compatible with `smooai-ui`: same constants, same paths, and the bare default build inherits the same dependency tree (none).

## Related repos

- [`SmooAI/ui`](https://github.com/SmooAI/ui) — the original design-system-only crate; [smooblue](https://github.com/SmooAI/smooblue) still consumes it directly. This repo carries the same `ui` surface for clients that also need `auth`.
- [`SmooAI/smooth`](https://github.com/SmooAI/smooth) — the `th` CLI; consumes `client-shared` (`features = ["auth"]`) for login + credential storage.
- `smooblue`, `observability-studio` — Dioxus desktop apps.
- [`SmooAI/ui`](https://github.com/SmooAI/ui) — **the design system**: tokens, base CSS, the smoo monogram. Consumed directly by [smooblue](https://github.com/SmooAI/smooblue) and observability-studio. This crate used to carry a copy; it no longer does.
- [`SmooAI/smooth`](https://github.com/SmooAI/smooth) — the `th` CLI; consumes this crate (`features = ["auth"]`) for login + credential storage.

## 🧩 Part of Smoo AI

Expand Down
33 changes: 10 additions & 23 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,27 @@ keywords = ["smooai", "design-system", "auth", "client"]
categories = ["gui", "authentication", "config"]
readme = "README.md"
authors = ["SmooAI <brent@smoo.ai>"]
# Cargo can't pull files from outside the package root by default; we
# explicitly enumerate the cross-language `shared/` assets we want
# included so `cargo package` produces a self-contained tarball.
include = [
"Cargo.toml",
"README.md",
"build.rs",
"src/**/*.rs",
"../shared/styles.css",
"../shared/monogram.svg",
"../shared/tokens.json",
"../shared/tokens_codegen.rs",
"../shared/tokens_css_check.rs",
"../LICENSE",
]

[features]
# Default to the lightweight `ui` module so existing smooai-ui
# consumers (smooblue, observability-studio) get the same dependency
# tree they had before — no tokio, no reqwest, still no_std-friendly.
# Opt into `auth` only when needed.
default = ["ui"]
ui = []
# `auth` is the crate's only surface and its one consumer (the `th` CLI)
# always asks for it — but it stays opt-in rather than default so the heavy
# tree (tokio, reqwest, axum) is never pulled in by accident.
#
# There was a `ui` feature here carrying the design system. Nothing ever
# imported `client_shared::ui`; SmooAI/ui owns that and is what smooblue and
# observability-studio actually depend on. Keeping a second copy behind a
# feature flag only created a drift surface, which had already silently
# shipped a broken monogram.
auth = ["dep:tokio", "dep:reqwest", "dep:serde", "dep:serde_json", "dep:url", "dep:axum", "dep:anyhow", "dep:chrono", "dep:dirs-next", "dep:webbrowser", "dep:rand", "dep:base64", "dep:sha2"]

[build-dependencies]
# Build-script only: parses shared/tokens.json to generate the `tokens`
# module. Build dependencies are host-side and never reach consumers, so the
# bare `ui` build stays zero-dependency and `no_std`.
serde_json = "1"

[dependencies]
# Heavy deps gated behind the `auth` feature so the bare `ui`
# build stays no_std-compatible and zero-dep.
# All gated behind the `auth` feature.
tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs", "net", "sync", "time"], optional = true }
reqwest = { version = "0.12", features = ["json"], optional = true }
serde = { version = "1", features = ["derive"], optional = true }
Expand Down
17 changes: 0 additions & 17 deletions rust/build.rs

This file was deleted.

61 changes: 23 additions & 38 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,41 @@
//! # smooai-client-shared
//!
//! Cross-runtime shared library for SmooAI Rust clients — the home for
//! design system primitives, auth flows, and LLM session plumbing that
//! every SmooAI Rust app needs identically. Consumed by `smooblue`
//! (Dioxus desktop), `observability-studio` (Dioxus viewer), `th` and
//! `smoo admin` (the Smooth CLI), and any future Rust client.
//!
//! Replaces the standalone `smooai-ui` crate (which only carried the
//! `ui` slice) by adding an `auth` module behind a feature flag. The bare `default-features = ["ui"]` build stays
//! `no_std`-compatible with zero runtime dependencies — same shape as
//! the old `smooai-ui` so existing consumers don't inherit any new
//! tree.
//! Auth primitives shared across SmooAI's Rust clients — Supabase user
//! OAuth (localhost-callback flow with PKCE), the M2M
//! `client_credentials` grant, refresh-token rotation, and an on-disk
//! `CredentialsStore`. Consumed by `th` and `smoo admin` (the Smooth
//! CLI).
//!
//! ## Feature flags
//!
//! - `ui` (default) — design tokens, base CSS, monogram. Zero deps,
//! `no_std`.
//! - `auth` — Supabase user OAuth (localhost-callback flow), M2M
//! `client_credentials` grant, refresh-token rotation, on-disk
//! `CredentialsStore`. Pulls in `tokio`, `reqwest`, `serde`, `axum`.
//! - `auth` — everything above. Pulls in `tokio`, `reqwest`, `serde`,
//! `axum`. Not a default: the dependency tree is heavy enough that it
//! should be asked for explicitly.
//!
//! ## What happened to the `ui` module
//!
//! This crate used to carry a copy of the design system (tokens, base
//! CSS, monogram) and describe itself as `smooai-ui`'s successor. That
//! migration was never finished, and an org-wide search found **nothing
//! importing `smooai_client_shared::ui`** — every real consumer of the
//! design system depends on [`SmooAI/ui`](https://github.com/SmooAI/ui)
//! directly (smooblue, observability-studio), while this crate's only
//! consumer asks for `auth` and never touched it.
//!
//! Two copies of the same files is a drift surface, and it had already
//! drifted: this crate spent weeks serving a monogram missing its inner
//! 'S' because a fix in SmooAI/ui never crossed. The copy is gone;
//! `SmooAI/ui` owns the design system.
//!
//! An `llm` feature (JWT → `llm.smoo.ai` org-scoped session exchange)
//! is planned under pearl th-f7b20f. It is deliberately **absent**
//! rather than stubbed: a feature flag that compiles to an empty
//! module advertises a capability that does not exist. It comes back
//! when there is something behind it.
//!
//! ## Migrating from `smooai-ui`
//!
//! Replace
//!
//! ```toml
//! smooai-ui = "0.1"
//! ```
//!
//! with
//!
//! ```toml
//! smooai-client-shared = "0.1"
//! ```
//!
//! and swap `smooai_ui::` → `smooai_client_shared::ui::` in your
//! imports. Everything in the `ui` module is re-exported at the same
//! path it lived at under `smooai_ui::` (e.g. `STYLES`,
//! `MONOGRAM_SVG`, `tokens::*`).

#![cfg_attr(not(feature = "auth"), no_std)]
#![doc(html_root_url = "https://docs.rs/smooai-client-shared/0.1.0")]
#![warn(missing_docs)]

#[cfg(feature = "ui")]
pub mod ui;

#[cfg(feature = "auth")]
pub mod auth;
Loading
Loading