Metadata-Version: 2.4
Name: msp-copilot
Version: 0.1.1
Summary: Safe, auditable MSP workflow automation CLI for Microsoft 365 operations.
Keywords: msp,automation,microsoft-graph,workflow,cli,audit
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Information Technology
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: System :: Systems Administration
Requires-Python: <3.14,>=3.10
Description-Content-Type: text/markdown
Requires-Dist: email-validator<3,>=2.2.0
Requires-Dist: msal<2,>=1.34.0
Requires-Dist: pydantic<3,>=2.10.0
Requires-Dist: python-dotenv<2,>=1.0.1
Requires-Dist: requests<3,>=2.32.3
Provides-Extra: dev
Requires-Dist: hypothesis>=6.120.0; extra == "dev"
Requires-Dist: mypy>=1.13.0; extra == "dev"
Requires-Dist: mutmut>=2.5.0; extra == "dev"
Requires-Dist: pytest>=8.3.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
Requires-Dist: ruff>=0.8.0; extra == "dev"
Requires-Dist: types-requests>=2.32.0.20241016; extra == "dev"

# msp-copilot

**Audit-grade workflow engine with approvals + traceability** | **Pilot-ready on a sandbox tenant** | **Architecture won't collapse when we swap mocks → real Graph**

## CLI quickstart

### Install

```bash
pip install msp-copilot
```

From source checkout:

```bash
pip install .
```

### Configure

Create a `.env` file (or set env vars in your shell):

```bash
# Required for real Graph runs (optional for mock/dry-run)
MSP_GRAPH_CLIENT_ID="..."
MSP_GRAPH_TENANT_ID="..."

# Safety: execution is OFF by default
MSP_EXECUTION_ENABLED="false"

# Optional: tenant aliases (tickets can use alias instead of GUID)
MSP_TENANT_ALIAS_cryingman-dev="67aca8ef-69a4-4853-9045-d4c345601d6e"
```

### Introspection commands (no auth / safe)

Explain a workflow:

```bash
msp-copilot explain offboarding
```

Validate a ticket (schema + guardrails only):

```bash
msp-copilot validate --ticket data/tickets/offboard_001.json
```

Preview a plan:

```bash
msp-copilot plan --ticket data/tickets/offboard_001.json
msp-copilot plan --ticket data/tickets/offboard_001.json --approve remove_licenses,convert_mailbox
```

Doctor checks (operator readiness):

```bash
msp-copilot doctor --tenant cryingman-dev --test-user john.doe@example.com
msp-copilot doctor --json
```

Live gauntlet (10-scenario operator check):

```bash
make test-live
```

Notes:

- Loads `.env` automatically when present
- Stores artifacts under `data/live_runs/<utc-timestamp>/`
- Scenario output logs: `data/live_runs/<utc-timestamp>/scenarios/`
- New run-log snapshot: `data/live_runs/<utc-timestamp>/run_logs/`
- Run metadata: `data/live_runs/<utc-timestamp>/meta.json` (commit/version/python/os/redacted env flags)
- Set `LIVE_GAUNTLET_USE_REAL=1` to keep real Graph credentials instead of forced mock mode

### Running workflows

Dry-run (default-safe):

```bash
msp-copilot run --ticket data/tickets/offboard_001.json --operator you
```

Execute (requires kill switch enabled):

```bash
MSP_EXECUTION_ENABLED=true \
msp-copilot run --ticket data/tickets/offboard_001.json --execute --operator you
```

Execute with approvals:

```bash
MSP_EXECUTION_ENABLED=true \
msp-copilot run --ticket data/tickets/offboard_001.json --execute \
  --approve remove_licenses,convert_mailbox \
  --operator you
```

Last run:

```bash
msp-copilot last-run
```

### Operator configuration

License removal configuration:

```json
{
  "ticket_id": "offboard_001",
  "type": "offboarding",
  "tenant_id": "cryingman-dev",
  "user_email": "john.doe@contoso.com",
  "remove_sku_ids": [
    "6fd2c87f-b296-42f0-b197-1e91e994b900"
  ]
}
```

or fallback defaults via env:

```bash
MSP_DEFAULT_REMOVE_SKU_IDS="6fd2c87f-b296-42f0-b197-1e91e994b900,another-sku-guid"
```

Rules:

- `remove_sku_ids` must be explicit (ticket or env fallback)
- Empty remove list fails the `remove_licenses` step
- Only targeted SKUs are removed
- Compensation payload is logged in run evidence

Exchange Online mode:

```bash
MSP_EXCHANGE_REAL=true
MSP_EXECUTION_ENABLED=true
```

Behavior:

- Uses `pwsh` subprocess commands for mailbox conversion
- Skips conversion when mailbox is already `SharedMailbox`
- Captures sanitized command output and timing in evidence
- `convert_mailbox` still requires explicit approval

### Group diff workflow

Config:

```bash
# Groups you allow the engine to manage (required for managed_only)
MSP_MANAGED_GROUP_IDS="guid1,guid2,guid3"

# Groups that must NEVER be removed
MSP_PROTECTED_GROUP_IDS="guidX,guidY"

# Safety cap on removals
MSP_GROUP_REMOVE_MAX="10"
```

Run (safe defaults):

```bash
msp-copilot plan --ticket data/tickets/group_diff_001.json
msp-copilot run  --ticket data/tickets/group_diff_001.json --operator you
```

Approve removals (if needed):

```bash
MSP_EXECUTION_ENABLED=true \
msp-copilot run --ticket data/tickets/group_diff_001.json --execute \
  --approve group_diff_apply \
  --operator you
```

Behavior rules:

- `desired_group_ids` must be non-empty
- In `managed_only`, only groups in `MSP_MANAGED_GROUP_IDS` are enforced
- Removals are blocked unless `--approve group_diff_apply` is provided
- Protected groups are never removed
- Removal cap prevents large accidental group removals

Note:

- `plan` does not compute `to_add`/`to_remove` counts yet; those are computed at execution precheck time

### Notes / behavior guarantees

- `--execute` is blocked unless `MSP_EXECUTION_ENABLED=true`
- Tenant and user guardrails run before any write steps
- Approval-required actions block unless explicitly approved
- Run logs are schema-validated, redacted, and versioned (`run_schema_version: 1`)

## Deployment

One-page deploy guide: `DEPLOYMENT.md`

## Current State

✅ **What you can claim right now:**
- Per-step audit trail with unique run_id (UUID v4)
- Approval gating for risky operations
- Real-time status tracking (planned → completed/blocked/failed/skipped)
- Enterprise audit logs: `data/runs/run_<timestamp>.json`
- Timestamps with millisecond precision (ISO 8601 UTC)
- Operator tracking via `MSP_OPERATOR_EMAIL` environment variable
- Architecture validated: swapping MockGraphOperations → real Graph API won't break the core engine

**Why "pilot-ready":**
- All core workflows tested and working (dry-run, execute, approval gates)
- Mock Graph API provides realistic 50ms latency per operation
- Audit logs capture everything needed for compliance review
- Run logs include: run_id, started_at, completed_at, duration_seconds, all step timings, approval tracking

**Why NOT "production-ready":**
- Still using mock Graph API (no real Microsoft 365 connectivity)
- No real OAuth authentication (we fake the login)
- No idempotency protection (reruns could conflict)
- Secrets still in .env (should be in keyring/vault)
- No cross-tenant isolation checks

## Enterprise Gates

See **ENTERPRISE_ROADMAP.md** for detailed implementation paths. The critical blockers for production:

1. **Real Microsoft 365 Auth + Tenant Isolation** ← Next priority (MSAL-based OAuth)
2. **Idempotency + Safe Retries** (SQLite store, retry policy)
3. **Secrets Management + Token Redaction** (keyring, log scrubbing)

## Documentation

- **ENTERPRISE_ROADMAP.md** — 6 production-readiness gates with code examples
- **QUICK_WINS.md** — 4 improvements just implemented (validation, run_id, timestamps, actor tracking)
- **NEXT_STEPS.md** — 30-day sprint plan + decision framework

## Architecture

**Domain-driven:** Ticket → Planner → Plan → Executor → RunResult + Auditor

**Connectors:** Microsoft Graph (mocked, real auth coming next)

**Storage:** JSON run logs + future SQLite (idempotency, audit)

**CLI:** Argparse with --ticket, --execute, --approve flags
