# Plan 4: SaaS Cleanup — Strip to Vendor Management Plane

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Remove all migrated code from the SaaS layer (environments, apps, deployments, ClickHouse access) and strip it down to a thin vendor management plane: tenant lifecycle, license generation, billing, and Logto organization management.

**Architecture:** The SaaS retains only vendor-level concerns. All runtime management, observability, and user management is now in the server. The SaaS communicates with server instances exclusively via REST API (ServerApiClient). ClickHouse dependency is removed entirely.

**Tech Stack:** Java 21, Spring Boot 3.4.3, PostgreSQL 16

**Repo:** `C:\Users\Hendrik\Documents\projects\cameleer-saas`

**Prerequisite:** Plans 1-3 must be implemented in cameleer-server first.

---

## Summary of Changes

### Files to DELETE (migrated to server or no longer needed)

```
src/main/java/net/siegeln/cameleer/saas/environment/
├── EnvironmentEntity.java
├── EnvironmentService.java
├── EnvironmentController.java
├── EnvironmentRepository.java
├── EnvironmentStatus.java
└── dto/
    ├── CreateEnvironmentRequest.java
    ├── UpdateEnvironmentRequest.java
    └── EnvironmentResponse.java

src/main/java/net/siegeln/cameleer/saas/app/
├── AppEntity.java
├── AppService.java
├── AppController.java
├── AppRepository.java
└── dto/
    ├── CreateAppRequest.java
    └── AppResponse.java

src/main/java/net/siegeln/cameleer/saas/deployment/
├── DeploymentEntity.java
├── DeploymentService.java
├── DeploymentController.java
├── DeploymentRepository.java
├── DeploymentExecutor.java
├── DesiredStatus.java
├── ObservedStatus.java
└── dto/
    └── DeploymentResponse.java

src/main/java/net/siegeln/cameleer/saas/runtime/
├── RuntimeOrchestrator.java
├── DockerRuntimeOrchestrator.java
├── RuntimeConfig.java
├── BuildImageRequest.java
├── StartContainerRequest.java
├── ContainerStatus.java
└── LogConsumer.java

src/main/java/net/siegeln/cameleer/saas/log/
├── ClickHouseConfig.java
├── ClickHouseProperties.java
├── ContainerLogService.java
├── LogController.java
└── dto/
    └── LogEntry.java

src/main/java/net/siegeln/cameleer/saas/observability/
├── AgentStatusService.java
├── AgentStatusController.java
└── dto/
    ├── AgentStatusResponse.java
    └── ObservabilityStatusResponse.java
```

### Files to MODIFY

```
src/main/java/net/siegeln/cameleer/saas/config/AsyncConfig.java          — remove deploymentExecutor bean
src/main/java/net/siegeln/cameleer/saas/tenant/TenantService.java        — remove createDefaultForTenant() call
src/main/resources/application.yml                                        — remove clickhouse + runtime config sections
docker-compose.yml                                                        — remove Docker socket mount from SaaS, update routing
```

### Files to KEEP (vendor management plane)

```
src/main/java/net/siegeln/cameleer/saas/tenant/          — Tenant CRUD, lifecycle
src/main/java/net/siegeln/cameleer/saas/license/         — License generation
src/main/java/net/siegeln/cameleer/saas/identity/        — Logto org management, ServerApiClient
src/main/java/net/siegeln/cameleer/saas/config/          — SecurityConfig, SpaController
src/main/java/net/siegeln/cameleer/saas/audit/           — Vendor audit logging
src/main/java/net/siegeln/cameleer/saas/apikey/          — API key management (if used)
ui/                                                       — Vendor management dashboard
```

### Flyway Migrations to KEEP

The existing migrations (V001-V009) can remain since they're already applied. Add a new cleanup migration:

```
src/main/resources/db/migration/V010__drop_migrated_tables.sql
```

---

### Task 1: Remove ClickHouse Dependency

- [ ] **Step 1: Delete ClickHouse files**

```bash
rm -rf src/main/java/net/siegeln/cameleer/saas/log/ClickHouseConfig.java
rm -rf src/main/java/net/siegeln/cameleer/saas/log/ClickHouseProperties.java
rm -rf src/main/java/net/siegeln/cameleer/saas/log/ContainerLogService.java
rm -rf src/main/java/net/siegeln/cameleer/saas/log/LogController.java
rm -rf src/main/java/net/siegeln/cameleer/saas/log/dto/
```

- [ ] **Step 2: Remove ClickHouse from AgentStatusService**

Delete `AgentStatusService.java` and `AgentStatusController.java` entirely (agent status is now a server concern).

```bash
rm -rf src/main/java/net/siegeln/cameleer/saas/observability/
```

- [ ] **Step 3: Remove ClickHouse config from application.yml**

Remove the entire `cameleer.clickhouse:` section.

- [ ] **Step 4: Remove ClickHouse JDBC dependency from pom.xml**

Remove:
```xml
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-jdbc</artifactId>
</dependency>
```

- [ ] **Step 5: Verify build**

Run: `cd /c/Users/Hendrik/Documents/projects/cameleer-saas && mvn compile`
Expected: BUILD SUCCESS. Fix any remaining import errors.

- [ ] **Step 6: Commit**

```bash
git add -A
git commit -m "feat: remove all ClickHouse dependencies from SaaS layer"
```

---

### Task 2: Remove Environment/App/Deployment Code

- [ ] **Step 1: Delete environment package**

```bash
rm -rf src/main/java/net/siegeln/cameleer/saas/environment/
```

- [ ] **Step 2: Delete app package**

```bash
rm -rf src/main/java/net/siegeln/cameleer/saas/app/
```

- [ ] **Step 3: Delete deployment package**

```bash
rm -rf src/main/java/net/siegeln/cameleer/saas/deployment/
```

- [ ] **Step 4: Delete runtime package**

```bash
rm -rf src/main/java/net/siegeln/cameleer/saas/runtime/
```

- [ ] **Step 5: Remove AsyncConfig deploymentExecutor bean**

In `AsyncConfig.java`, remove the `deploymentExecutor` bean (or delete AsyncConfig if it only had that bean).

- [ ] **Step 6: Update TenantService**

Remove any calls to `EnvironmentService.createDefaultForTenant()` from `TenantService.java`. The server now handles default environment creation.

- [ ] **Step 7: Remove runtime config from application.yml**

Remove the entire `cameleer.runtime:` section.

- [ ] **Step 8: Verify build**

Run: `cd /c/Users/Hendrik/Documents/projects/cameleer-saas && mvn compile`
Expected: BUILD SUCCESS. Fix any remaining import errors.

- [ ] **Step 9: Commit**

```bash
git add -A
git commit -m "feat: remove migrated environment/app/deployment/runtime code from SaaS"
```

---

### Task 3: Database Cleanup Migration

- [ ] **Step 1: Create cleanup migration**

```sql
-- V010__drop_migrated_tables.sql
-- Drop tables that have been migrated to cameleer-server

DROP TABLE IF EXISTS deployments CASCADE;
DROP TABLE IF EXISTS apps CASCADE;
DROP TABLE IF EXISTS environments CASCADE;
DROP TABLE IF EXISTS api_keys CASCADE;
```

- [ ] **Step 2: Commit**

```bash
git add src/main/resources/db/migration/V010__drop_migrated_tables.sql
git commit -m "feat: drop migrated tables from SaaS database"
```

---

### Task 4: Remove Docker Socket Dependency

- [ ] **Step 1: Update docker-compose.yml**

Remove from `cameleer-saas` service:
```yaml
volumes:
  - /var/run/docker.sock:/var/run/docker.sock
  - jardata:/data/jars
group_add:
  - "0"
```

The Docker socket mount now belongs to the `cameleer-server` service instead.

- [ ] **Step 2: Remove docker-java dependency from pom.xml**

Remove:
```xml
<dependency>
    <groupId>com.github.docker-java</groupId>
    <artifactId>docker-java-core</artifactId>
</dependency>
<dependency>
    <groupId>com.github.docker-java</groupId>
    <artifactId>docker-java-transport-zerodep</artifactId>
</dependency>
```

- [ ] **Step 3: Commit**

```bash
git add docker-compose.yml pom.xml
git commit -m "feat: remove Docker socket dependency from SaaS layer"
```

---

### Task 5: Update SaaS UI

- [ ] **Step 1: Remove environment/app/deployment pages from SaaS frontend**

Remove pages that now live in the server UI:
- `EnvironmentsPage`
- `EnvironmentDetailPage`
- `AppDetailPage`

The SaaS UI retains:
- `DashboardPage` — vendor overview (tenant list, status)
- `AdminTenantsPage` — tenant management
- `LicensePage` — license management

- [ ] **Step 2: Update navigation**

Remove links to environments/apps/deployments. The SaaS UI should link to the tenant's server instance for those features (e.g., "Open Dashboard" link to `https://{tenant-slug}.cameleer.example.com/server/`).

- [ ] **Step 3: Commit**

```bash
git add ui/
git commit -m "feat: strip SaaS UI to vendor management dashboard"
```

---

### Task 6: Expand ServerApiClient

- [ ] **Step 1: Add provisioning-related API calls**

The `ServerApiClient` should gain methods for tenant provisioning:

```java
public void pushLicense(String serverEndpoint, String licenseToken) {
    post(serverEndpoint + "/api/v1/admin/license")
            .body(Map.of("token", licenseToken))
            .retrieve()
            .toBodilessEntity();
}

public Map<String, Object> getHealth(String serverEndpoint) {
    return get(serverEndpoint + "/api/v1/health")
            .retrieve()
            .body(Map.class);
}
```

- [ ] **Step 2: Commit**

```bash
git add src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java
git commit -m "feat: expand ServerApiClient with license push and health check methods"
```

---

### Task 7: Write SAAS-INTEGRATION.md

- [ ] **Step 1: Create integration contract document**

Create `docs/SAAS-INTEGRATION.md` in the cameleer-server repo documenting:
- Which server API endpoints the SaaS calls
- Required auth (M2M token with `server:admin` scope)
- License injection mechanism (`POST /api/v1/admin/license`)
- Health check endpoint (`GET /api/v1/health`)
- What the server exposes vs what the SaaS must never access directly
- Env vars the SaaS sets when provisioning a server instance

- [ ] **Step 2: Commit**

```bash
cd /c/Users/Hendrik/Documents/projects/cameleer-server
git add docs/SAAS-INTEGRATION.md
git commit -m "docs: add SaaS integration contract documentation"
```

---

### Task 8: Final Verification

- [ ] **Step 1: Build SaaS**

Run: `cd /c/Users/Hendrik/Documents/projects/cameleer-saas && mvn clean verify`
Expected: BUILD SUCCESS with reduced dependency footprint.

- [ ] **Step 2: Verify SaaS starts without ClickHouse**

The SaaS should start with only PostgreSQL (and Logto). No ClickHouse required.

- [ ] **Step 3: Verify remaining code footprint**

The SaaS source should now contain approximately:
- `tenant/` — ~4 files
- `license/` — ~5 files
- `identity/` — ~3 files (LogtoConfig, ServerApiClient, M2M token)
- `config/` — ~3 files (SecurityConfig, SpaController, TLS)
- `audit/` — ~3 files
- `ui/` — stripped dashboard

Total: ~20 Java files (down from ~75).

- [ ] **Step 4: Final commit**

```bash
git add -A
git commit -m "chore: finalize SaaS cleanup — vendor management plane only"
```
