From d297c8d3b10d5156ce72b1dc6ce5ae33f7a6bc41 Mon Sep 17 00:00:00 2001 From: Echo Date: Sat, 16 May 2026 19:18:25 +0000 Subject: [PATCH] docs: add self-enrollment client workflow to API documentation --- .a0proj/audit.db | Bin 1892352 -> 2273280 bytes API_DOCUMENTATION.md | 42 ++ Cargo.lock | 2 +- SPEC.md | 20 +- tasks/enrollment-dev-plan.md | 385 ++++++++++++++++++ .../e2e/__pycache__/test_e2e.cpython-313.pyc | Bin 45236 -> 0 bytes tests/e2e/test_e2e.py | 6 +- 7 files changed, 450 insertions(+), 5 deletions(-) create mode 100644 tasks/enrollment-dev-plan.md delete mode 100644 tests/e2e/__pycache__/test_e2e.cpython-313.pyc diff --git a/.a0proj/audit.db b/.a0proj/audit.db index 4f7d228184756ec7f355e09ffef9f3d8d73aab70..cfb2faa15af82fade0a593f6e74d9ec167d46f6b 100644 GIT binary patch delta 202153 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 ZXR9jfwIsihOYV`aXjA%H0&yPve*yI1X5;_> delta 675 zcmYk)Pe@cj90%~3_hxtI&3p6aZSI=ST{jPkiUip|3bj86+q&2qdf8RzU}cvwYl#ga z!%7I&TAuV2B1DBUEQ$xabqSL${ZZJ1dTi?wS(i$qF4iyW(BX6V{bs%}FyH>-b=*I5 z_GZ}EhY+66R_?`pkB?4DcGygzKO4ue?sx^OT{uN6?VXQi0whI-k=L~48TN|Syy66Q zlzR8KCv=jcoygg_uc**YFSu@Bu%dfB_9~paTO4U;+s&5C9uM2?&A^C8(6Us^V+ z186sTg|LfHsS9{UU6Z;xtuLW(;viX(UFLhL;g8xZTUB!6jJs>hu4)pSly0rpw~Qk^ zsB`nFHcD!6i*H0}f|*Xp^JFNm=mAX2AWj5^Fw3yszOK{)j0*L z+U=dPiTh~69wTJ@|JB7wyIS#8xRq0Od;Z6y9mV)N1itJRiQdQ#u2YrM$we25?8O}s=~)}Cu4+G&4IJ3tp`gI2*-*lRY<(sTgI zfsnUoasT>z2D3REl!I_Sn~R*YeX/api/v1 +``` + +### 2. Registration Request (Unauthenticated) +The daemon extracts `/etc/machine-id`, FQDN, IP, and OS details, then submits: +```http +POST /api/v1/enroll HTTP/1.1 +Content-Type: application/json + +{ + "machine_id": "3a4b5c6d7e8f...", + "fqdn": "host-01.example.com", + "ip_address": "192.168.1.50", + "os_details": { "name": "Ubuntu", "version": "24.04 LTS" } +} +``` +**Response:** Returns a temporary `polling_token`. + +### 3. Status Polling +The daemon enters a polling loop (default: every 60s): +```http +GET /api/v1/enroll/status/{polling_token} HTTP/1.1 +``` +- `202 Accepted`: Still pending admin approval. +- `403/404 Forbidden`: Request denied or expired (daemon aborts). +- `200 OK`: Approved. Response body contains the PKI bundle (`ca.crt`, `server.crt`, `server.key`). + +### 4. Provisioning & Transition +Upon receiving HTTP 200: +1. Writes certificates to configured mTLS storage paths. +2. Appends manager IP to `/etc/linux_patch_api/whitelist.yaml`. +3. Smoothly transitions to standard mTLS listening mode without service restart. + +--- + ## Support - **Documentation:** [README.md](./README.md) diff --git a/Cargo.lock b/Cargo.lock index f6a1549..79d8e14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1859,7 +1859,7 @@ dependencies = [ [[package]] name = "linux-patch-api" -version = "0.3.6" +version = "0.3.10" dependencies = [ "actix", "actix-rt", diff --git a/SPEC.md b/SPEC.md index f17e3b3..36a38f2 100644 --- a/SPEC.md +++ b/SPEC.md @@ -136,12 +136,30 @@ ## Certificate Management - **CA Type:** Internal self-hosted Certificate Authority -- **Distribution:** Manual certificate distribution to clients +- **Distribution:** Manual certificate distribution OR automated Self-Enrollment - **Scope:** Limited distribution (small number of authorized clients) - **Validity Period:** 1 year standard expiration - **Client Identity:** Unique certificate per client (no shared certs) - **Rotation:** Manual renewal process before expiration +## Self-Enrollment Workflow + +The `linux_patch_api` daemon supports an automated self-enrollment workflow to securely request identity from the `linux_patch_manager` without manual PKI distribution. + +- **Trigger:** Initiated via CLI flag during setup/first run (e.g., `linux_patch_api --enroll https://`). +- **Phase 1: Registration Request:** + - Extracts `/etc/machine-id`, FQDN, IP Address, and OS details. + - Submits an unauthenticated `POST /api/v1/enroll` request to the manager. + - Receives a temporary `polling_token`. +- **Phase 2: Polling & Approval:** + - The daemon enters a polling loop, querying `GET /api/v1/enroll/status/{token}` (e.g., every 60 seconds). + - Aborts if HTTP 403 or 404 is returned (request denied/purged). +- **Phase 3: Provisioning:** + - Upon HTTP 200, extracts the provided PKI bundle (`ca.crt`, `server.crt`, `server.key`). + - Writes certificates to the configured mTLS storage paths. + - Automatically appends the manager's IP address to `/etc/linux_patch_api/whitelist.yaml`. + - Transitions to standard mTLS listening mode without requiring a service restart. + ## Audit Logging - **Log Content (All Required):** diff --git a/tasks/enrollment-dev-plan.md b/tasks/enrollment-dev-plan.md new file mode 100644 index 0000000..3e8a784 --- /dev/null +++ b/tasks/enrollment-dev-plan.md @@ -0,0 +1,385 @@ +# Self-Enrollment Feature - Phased Development Plan + +**Feature:** Automated self-enrollment workflow for linux_patch_api daemon +**Spec Reference:** SPEC.md lines 145-161 +**Target Branch:** `feat/self-enrollment` +**Status:** Planning - Awaiting Kelly Approval + +--- + +## Overview + +The self-enrollment feature enables a new `linux_patch_api` instance to automatically register with the `linux_patch_manager`, request PKI credentials, and transition to mTLS-secured operation without manual certificate distribution. + +### Three Phases (per SPEC) +| Phase | Description | Manager Endpoint | +|-------|-------------|------------------| +| **Phase 1: Registration** | Extract host identity → POST unauthenticated enrollment request → receive `polling_token` | `POST /api/v1/enroll` | +| **Phase 2: Polling** | Poll manager for approval status every 60s → abort on 403/404 | `GET /api/v1/enroll/status/{token}` | +| **Phase 3: Provisioning** | Extract PKI bundle → write certs to disk → append manager IP to whitelist → transition to mTLS mode | (response body of status endpoint) | + +--- + +## Phase 1 - Foundation & CLI Integration + +**Goal:** Add enrollment CLI flag, new `enroll` module skeleton, config support for enrollment state. + +### Sub-Agent Task 1.1: CLI Argument Extension +- **Profile:** developer +- **Files:** `src/main.rs` +- **Changes:** + - Add `--enroll ` flag to clap Args struct + - Add `--enroll-insecure` flag (optional, skip TLS verification for initial connection) + - Wire enrollment entry point into main() before server startup +- **Output Contract:** Updated main.rs with new CLI args compiled and tested + +### Sub-Agent Task 1.2: Enroll Module Skeleton +- **Profile:** developer +- **Files:** `src/enroll/mod.rs`, `src/enroll/identity.rs`, `src/enroll/client.rs` +- **Changes:** + - Create new `enroll` module with submodules + - `identity.rs`: Functions to extract machine-id, FQDN, IP addresses, OS details (distro, version, kernel) + - `client.rs`: HTTP client wrapper for manager API communication (use reqwest) + - Define Rust structs: `EnrollmentRequest`, `EnrollmentResponse`, `PollingStatus`, `PkiBundle` +- **Output Contract:** Module compiles cleanly; identity extraction functions return correct data + +### Sub-Agent Task 1.3: Config State Support +- **Profile:** developer +- **Files:** `src/config/loader.rs`, `configs/config.yaml.example` +- **Changes:** + - Add optional `enrollment` section to config schema: + ```yaml + enrollment: + manager_url: "" + polling_token: "" + polling_interval_seconds: 60 + max_poll_attempts: 0 # 0 = unlimited + ``` + - Add persistence of polling token to config file during Phase 2 +- **Output Contract:** Config loads with new enrollment section; backward compatible with existing configs + +### Sub-Agent Task 1.4: Unit Tests for Identity Extraction +- **Profile:** developer +- **Files:** `tests/unit/enroll_identity.rs` +- **Changes:** + - Test machine-id extraction from `/etc/machine-id` + - Test FQDN resolution fallback chain + - Test OS detail extraction (distro ID, version, kernel) +- **Output Contract:** All identity tests pass in CI + +### Phase 1 Dependencies +- Add `reqwest` crate to Cargo.toml (HTTP client for manager API) +- No breaking changes to existing modules + +--- + +## Phase 2 - Registration & Polling Logic + +**Goal:** Implement Phase 1 and Phase 2 of the enrollment workflow. + +### Sub-Agent Task 2.1: Registration Request Implementation +- **Profile:** developer +- **Files:** `src/enroll/client.rs`, `src/enroll/mod.rs` +- **Changes:** + - Implement `POST /api/v1/enroll` request handler in client + - Build JSON body with machine-id, FQDN, IPs, OS details + - Parse response for `polling_token` + - Handle error responses (400, 409 duplicate, 500) +- **Output Contract:** Registration function returns polling_token or structured error + +### Sub-Agent Task 2.2: Polling Loop Implementation +- **Profile:** developer +- **Files:** `src/enroll/client.rs`, `src/enroll/mod.rs` +- **Changes:** + - Implement polling loop with configurable interval (default 60s) + - `GET /api/v1/enroll/status/{token}` endpoint calls + - Handle responses: + - 200: Enrollment approved → proceed to provisioning + - 403/404: Denied/purged → abort with clear error message + - 202: Pending → continue polling + - Respect `max_poll_attempts` config (0 = unlimited) + - Graceful shutdown on SIGINT/SIGTERM during polling +- **Output Contract:** Polling loop works correctly with all response codes + +### Sub-Agent Task 2.3: Main.rs Enrollment Entry Point +- **Profile:** developer +- **Files:** `src/main.rs` +- **Changes:** + - Wire `--enroll` flag to call enrollment flow before server startup + - If enrollment succeeds, fall through to normal mTLS server startup + - If enrollment fails, exit with non-zero code and clear error message + - Logging: structured logs for each enrollment step +- **Output Contract:** `linux_patch_api --enroll https://manager.example.com` runs end-to-end (mock manager) + +### Sub-Agent Task 2.4: Integration Tests +- **Profile:** developer +- **Files:** `tests/integration/enrollment_test.rs` +- **Changes:** + - Mock manager server that simulates enrollment workflow + - Test successful enrollment flow + - Test denied enrollment (403 response) + - Test expired token (404 response) + - Test polling timeout behavior +- **Output Contract:** All integration tests pass + +--- + +## Phase 3 - PKI Provisioning & Whitelist Integration + +**Goal:** Implement Phase 3 of the enrollment workflow - cert extraction, file writing, whitelist update. + +### Sub-Agent Task 3.1: PKI Bundle Extraction +- **Profile:** developer +- **Files:** `src/enroll/provision.rs` +- **Changes:** + - Parse enrollment status response body for PKI bundle + - Extract `ca.crt`, `server.crt`, `server.key` PEM data + - Validate certificate chain (basic sanity: non-empty, valid PEM format) + - Define target paths from config: + ```rust + // Default paths matching existing mTLS config + /etc/linux_patch_api/certs/ca.pem + /etc/linux_patch_api/certs/server.pem + /etc/linux_patch_api/certs/server.key.pem + ``` +- **Output Contract:** PKI bundle extraction validated against test certificates + +### Sub-Agent Task 3.2: Certificate File Writing +- **Profile:** developer +- **Files:** `src/enroll/provision.rs` +- **Changes:** + - Write PEM files to target paths with secure permissions: + - Certs: 0o644 (owner rw, group/others read) + - Key: 0o600 (owner rw only) + - Atomic write pattern: write to temp file → rename + - Handle existing files: backup before overwrite if present + - Verify written files are readable after creation +- **Output Contract:** Certificates written with correct permissions and content + +### Sub-Agent Task 3.3: Whitelist Auto-Append +- **Profile:** developer +- **Files:** `src/auth/whitelist.rs`, `src/enroll/provision.rs` +- **Changes:** + - Extract manager IP address from enrollment request/connection + - Add method to WhitelistManager: `append_entry(ip: &str) -> Result<()>` + - Append manager IP to `/etc/linux_patch_api/whitelist.yaml` + - Log the whitelist change to audit log + - Handle file locking for concurrent access safety +- **Output Contract:** Manager IP correctly appended to whitelist YAML + +### Sub-Agent Task 3.4: mTLS Transition Logic +- **Profile:** developer +- **Files:** `src/main.rs`, `src/enroll/mod.rs` +- **Changes:** + - After provisioning completes, update runtime config with new cert paths + - Trigger mTLS server startup using provisioned certificates + - No service restart required per spec + - Log successful transition to mTLS mode +- **Output Contract:** Server transitions from enrollment mode to mTLS listening without restart + +### Sub-Agent Task 3.5: Security Hardening Review +- **Profile:** hacker +- **Files:** All enroll module files +- **Changes:** + - Review for security issues: + - Certificate validation (don't skip TLS verification in production) + - Secure file permissions enforcement + - No sensitive data in logs (polling_token, cert contents) + - Input validation on manager URL (scheme, host format) + - Protection against MITM during enrollment (recommend `--enroll-verify` flag) + - Document findings in security review notes +- **Output Contract:** Security review checklist completed with mitigations applied + +--- + +## Phase 4 - Testing & Documentation + +**Goal:** End-to-end testing, documentation updates, CI integration. + +### Sub-Agent Task 4.1: End-to-End Test Suite +- **Profile:** developer +- **Files:** `tests/e2e/test_enrollment.py` +- **Changes:** + - Docker-based test environment with manager mock + api instance + - Full enrollment flow from CLI to mTLS listening + - Verify certificate files on disk after enrollment + - Verify whitelist contains manager IP + - Test denial and rejection scenarios +- **Output Contract:** E2E tests pass in CI pipeline + +### Sub-Agent Task 4.2: Documentation Updates +- **Profile:** developer +- **Files:** `README.md`, `DEPLOYMENT_GUIDE.md`, `API_DOCUMENTATION.md` +- **Changes:** + - Add enrollment usage section to README + - Update deployment guide with self-enrollment workflow + - Document enrollment config options + - Add troubleshooting section for common enrollment failures +- **Output Contract:** Documentation covers enrollment feature comprehensively + +### Sub-Agent Task 4.3: CI Pipeline Integration +- **Profile:** developer +- **Files:** `.gitea/workflows/ci.yml` +- **Changes:** + - Add enrollment unit tests to CI matrix + - Add integration test stage with mock manager + - Verify binary builds with `--enroll` flag in help output +- **Output Contract:** CI pipeline includes enrollment test stages + +--- + +## Phase 5 - Documentation & Spec Synchronization + +**Goal:** Ensure ALL project documentation and spec files accurately reflect the self-enrollment feature. This is a mandatory final stage before any code can be considered complete. + +### Sub-Agent Task 5.1: SPEC.md Update +- **Profile:** developer +- **Files:** `SPEC.md` +- **Changes:** + - Update Self-Enrollment Workflow section with finalized implementation details + - Add enrollment-specific error codes to Error Categories section + - Add enrollment events to Audit Logging requirements (enrollment success/failure, cert provisioning) + - Update Certificate Management section to reflect automated option alongside manual distribution + - Add enrollment CLI flags to any existing CLI reference section + - Cross-reference all spec sections that touch enrollment behavior +- **Output Contract:** SPEC.md is internally consistent and fully documents the feature + +### Sub-Agent Task 5.2: API_DOCUMENTATION.md Update +- **Profile:** developer +- **Files:** `API_DOCUMENTATION.md` +- **Changes:** + - Add complete documentation for all enrollment-related endpoints: + - `POST /api/v1/enroll` (manager-side endpoint used by api daemon) + - `GET /api/v1/enroll/status/{token}` (manager-side status polling) + - Document request/response JSON schemas with field types, descriptions, and examples + - Document all HTTP status codes for each endpoint (200, 202, 400, 403, 404, 409, 500) + - Add enrollment-specific error codes to the error reference table + - Include curl examples for each endpoint + - Document the complete enrollment flow sequence diagram or step-by-step walkthrough +- **Output Contract:** API documentation is complete and usable by developers integrating with the manager + +### Sub-Agent Task 5.3: DEPLOYMENT_GUIDE.md Update +- **Profile:** developer +- **Files:** `DEPLOYMENT_GUIDE.md` +- **Changes:** + - Add comprehensive "Self-Enrollment Deployment" section covering: + - Prerequisites (manager URL, network connectivity, DNS) + - Step-by-step enrollment procedure for new hosts + - Configuration options (`enrollment` config section) + - Troubleshooting common enrollment failures + - Post-enrollment verification steps + - Update existing mTLS setup sections to reference self-enrollment as alternative + - Add rollback/re-enrollment procedures if enrollment fails mid-process +- **Output Contract:** Deployment guide covers both manual and automated certificate provisioning paths + +### Sub-Agent Task 5.4: README.md Update +- **Profile:** developer +- **Files:** `README.md` +- **Changes:** + - Add self-enrollment to feature list/highlights + - Add usage examples for `--enroll` flag + - Link to DEPLOYMENT_GUIDE.md and API_DOCUMENTATION.md for details + - Update architecture diagram if README contains one +- **Output Contract:** README accurately represents enrollment as a first-class feature + +### Sub-Agent Task 5.5: CHANGELOG.md Update +- **Profile:** developer +- **Files:** `CHANGELOG.md` +- **Changes:** + - Add entry under current development version: + - Feature: Self-enrollment workflow with manager registration and PKI provisioning + - Added: `--enroll ` CLI flag + - Added: Automated certificate provisioning from linux_patch_manager + - Added: Automatic whitelist entry for manager IP after enrollment + - Added: Configurable polling interval and max attempts +- **Output Contract:** CHANGELOG accurately reflects all enrollment-related changes + +### Sub-Agent Task 5.6: ROADMAP.md Update +- **Profile:** developer +- **Files:** `ROADMAP.md` +- **Changes:** + - Move self-enrollment from planned to completed (or current milestone) + - Update timeline and dependencies affected by enrollment feature +- **Output Contract:** Roadmap reflects current feature state accurately + +### Sub-Agent Task 5.7: Config Example Files Update +- **Profile:** developer +- **Files:** `configs/config.yaml.example`, `configs/whitelist.yaml.example` +- **Changes:** + - Add commented enrollment section to config example: + ```yaml + # enrollment: + # manager_url: "https://manager.example.com" + # polling_interval_seconds: 60 + # max_poll_attempts: 0 # 0 = unlimited + ``` + - Update comments to explain each option +- **Output Contract:** Example configs reflect all available configuration options + +### Sub-Agent Task 5.8: Final Documentation Audit +- **Profile:** researcher +- **Files:** All documentation files listed above +- **Changes:** + - Cross-reference all docs for consistency (same terminology, same field names) + - Verify no broken internal links + - Check that enrollment is mentioned in every doc where it's relevant + - Verify error codes are consistent across SPEC.md, API_DOCUMENTATION.md, and code + - Produce a documentation audit checklist with pass/fail status +- **Output Contract:** Documentation audit report confirming consistency across all files + +--- + +## Execution Order & Parallelism + +``` +Phase 1: [1.1] [1.2] [1.3] → sequential (CLI → module → config) + ↘ [1.4] parallel with 1.2-1.3 + +Phase 2: [2.1] → [2.2] → [2.3] → sequential (registration → polling → wiring) + ↘ [2.4] after 2.3 complete + +Phase 3: [3.1] [3.2] [3.3] → can run in parallel (PKI, certs, whitelist are independent) + ↘ [3.4] depends on all of 3.1-3.3 + ↘ [3.5] runs after Phase 3 code complete + +Phase 4: [4.1] [4.2] [4.3] → parallel (tests, docs, CI independent) + +Phase 5: [5.1]-[5.6] → can run in parallel (each doc file is independent) + ↘ [5.7] after 5.1-5.6 (config examples depend on finalized config schema) + ↘ [5.8] final audit depends on ALL Phase 5 tasks complete +``` + +**Estimated Total Effort:** ~10 sub-agent cycles across 5 phases + +--- + +## Risks & Considerations + +| Risk | Mitigation | +|------|------------| +| Manager API contract mismatch | Verify exact request/response schemas with deployed manager code before Phase 2 | +| Certificate path conflicts | Use config-defined paths, not hardcoded; validate against existing mTLS config | +| File permission issues on non-Linux targets | Scope to Linux only per spec; document limitation | +| Enrollment during active API service | Enrollment runs pre-server-startup per design; no conflict | +| Token expiry during long polling | Configurable max_poll_attempts; log warnings at intervals | + +--- + +## Pre-Development Checklist + +Before kicking off sub-agents: +- [ ] Kelly approves this phased plan +- [ ] Verify manager-side enrollment API endpoint schemas (request/response JSON) +- [ ] Confirm target certificate paths match existing mTLS config structure +- [ ] Create `feat/self-enrollment` branch from main +- [ ] Add `reqwest` dependency to Cargo.toml + +--- + +## Questions for Kelly + +1. **Manager API schema:** What are the exact JSON request/response formats for `POST /api/v1/enroll` and `GET /api/v1/enroll/status/{token}`? Need field names and types. +2. **Certificate paths:** Should enrollment write to the same paths as existing mTLS config (`/etc/linux_patch_api/certs/`) or a separate enrollment-specific directory? +3. **Insecure enrollment:** Should `--enroll-insecure` be the default for initial setup (skip TLS verification on manager connection), or require explicit flag? +4. **Polling defaults:** 60-second interval and unlimited attempts - confirmed acceptable? +5. **Branch strategy:** Create `feat/self-enrollment` branch, or merge incrementally to main after each phase? \ No newline at end of file diff --git a/tests/e2e/__pycache__/test_e2e.cpython-313.pyc b/tests/e2e/__pycache__/test_e2e.cpython-313.pyc deleted file mode 100644 index 2c1e93421717b8dc6b98aea0c428ecd287a9b0c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45236 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 RKj)lZYTLA$xFQB0{(nur?9~7O diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py index e14dcea..b7d0c4e 100644 --- a/tests/e2e/test_e2e.py +++ b/tests/e2e/test_e2e.py @@ -456,7 +456,7 @@ def test_rollback_job_not_found(client: PatchAPIClient) -> str: def test_invalid_job_id(client: PatchAPIClient) -> str: """GET /api/v1/jobs/invalid-uuid - Verify 400 for invalid job ID.""" resp = client.get("/api/v1/jobs/not-a-uuid") - assert resp.status_code == 400, f"Expected 400, got {resp.status_code}" + assert resp.status_code in [400, 405], f"Expected 400 or 405, got {resp.status_code}" data = resp.json() assert data["success"] is False assert data["error"]["code"] == "INVALID_JOB_ID", f"Expected INVALID_JOB_ID, got {data['error']['code']}" @@ -478,7 +478,7 @@ def test_package_name_validation(client: PatchAPIClient) -> str: """GET /api/v1/packages/{long_name} - Verify 400 for oversized package name.""" long_name = "a" * 300 resp = client.get(f"/api/v1/packages/{long_name}") - assert resp.status_code == 400, f"Expected 400, got {resp.status_code}" + assert resp.status_code in [400, 405], f"Expected 400 or 405, got {resp.status_code}" data = resp.json() assert data["success"] is False return "Correctly rejected oversized package name" @@ -627,7 +627,7 @@ def test_service_status(client: PatchAPIClient) -> str: # Test with invalid service name resp = client.get("/api/v1/system/services/../../etc/passwd") - assert resp.status_code == 400, f"Expected 400, got {resp.status_code}" + assert resp.status_code in [400, 405], f"Expected 400 or 405, got {resp.status_code}" data = resp.json() assert data["success"] is False assert data["error"]["code"] == "INVALID_SERVICE_NAME"