diff --git a/.claude/rules/core-classes.md b/.claude/rules/core-classes.md index 8d6db545..5c2e9351 100644 --- a/.claude/rules/core-classes.md +++ b/.claude/rules/core-classes.md @@ -47,14 +47,19 @@ paths: ## license/ — License domain (signed-token tier system) +The pure license **contract types** live in the separate `cameleer-license-api` module under package `com.cameleer.license` (no Spring, no server-runtime deps) so consumers like `cameleer-license-minter` and `cameleer-saas` can use them without inheriting server internals. Server-core only contains the runtime state holder (`LicenseGate`). + +Contract types in `cameleer-license-api` (package `com.cameleer.license`): - `LicenseInfo` — record: `(UUID licenseId, String tenantId, String label, Map limits, Instant issuedAt, Instant expiresAt, int gracePeriodDays)`. `isExpired()` true once `now > expiresAt + gracePeriodDays`; `isAfterRawExpiry()` true once `now > expiresAt`. Constructed via `LicenseValidator`; canonical ctor null-checks all required fields and rejects blank tenantId / negative grace. - `LicenseLimits` — typed limits container backed by `Map`. `defaultsOnly()` returns the `DefaultTierLimits.DEFAULTS` view; `mergeOverDefaults(overrides)` produces the license-overrides UNION default tier. `get(String key)` returns the cap; throws `IllegalArgumentException` for unknown keys (programmer error). `isDefaultSourced(key, license)` reports whether a key fell through to the default tier. - `DefaultTierLimits` — immutable `LinkedHashMap` of constants for the no-license fallback tier: `max_environments=1, max_apps=3, max_agents=5, max_users=3, max_outbound_connections=1, max_alert_rules=2, max_total_cpu_millis=2000, max_total_memory_mb=2048, max_total_replicas=5, max_execution_retention_days=1, max_log_retention_days=1, max_metric_retention_days=1, max_jar_retention_count=3`. - `LicenseValidator` — verifies signed token. Constructor `(String publicKeyBase64, String expectedTenantId)` decodes an X.509 Ed25519 public key. `validate(String token)` splits `payload.signature`, verifies the Ed25519 signature, parses the JSON payload, enforces `tenantId == expectedTenantId`, and returns `LicenseInfo`. Throws `SecurityException` on signature mismatch / `IllegalArgumentException` on parse failure / expired payload. -- `LicenseGate` — runtime state holder (thread-safe via `AtomicReference`). `getCurrent()` returns the current `LicenseInfo` (null when ABSENT/INVALID); `getState()` delegates to `LicenseStateMachine.classify(...)`; `getEffectiveLimits()` returns license-overrides UNION defaults in `ACTIVE`/`GRACE`, defaults-only otherwise. `getInvalidReason()`, `load(LicenseInfo)`, `markInvalid(String reason)`, `clear()` are the mutators. `getLimit(key, defaultValue)` shorthand swallows unknown-key errors. - `LicenseStateMachine` — pure classifier. `classify(LicenseInfo, String invalidReason)` returns `INVALID` if a reason is set, `ABSENT` if no license, `ACTIVE` if `now <= expiresAt`, `GRACE` if expired but within grace window, `EXPIRED` otherwise. - `LicenseState` — enum: `ABSENT, ACTIVE, GRACE, EXPIRED, INVALID`. +Runtime state holder in server-core (package `com.cameleer.server.core.license`): +- `LicenseGate` — runtime state holder (thread-safe via `AtomicReference`). `getCurrent()` returns the current `LicenseInfo` (null when ABSENT/INVALID); `getState()` delegates to `LicenseStateMachine.classify(...)`; `getEffectiveLimits()` returns license-overrides UNION defaults in `ACTIVE`/`GRACE`, defaults-only otherwise. `getInvalidReason()`, `load(LicenseInfo)`, `markInvalid(String reason)`, `clear()` are the mutators. `getLimit(key, defaultValue)` shorthand swallows unknown-key errors. + ## search/ — Execution search and stats - `SearchService` — search, count, stats, statsForApp, statsForRoute, timeseries, timeseriesForApp, timeseriesForRoute, timeseriesGroupedByApp, timeseriesGroupedByRoute, slaCompliance, slaCountsByApp, slaCountsByRoute, topErrors, activeErrorTypes, punchcard, distinctAttributeKeys. `statsForRoute`/`timeseriesForRoute` take `(routeId, applicationId)` — app filter is applied to `stats_1m_route`. diff --git a/CLAUDE.md b/CLAUDE.md index b27217d2..bc2a8465 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,8 +14,10 @@ Cameleer Server — observability server that receives, stores, and serves Camel ## Modules +- `cameleer-license-api` — pure license contract types (`LicenseInfo`, `LicenseValidator`, `LicenseState`, `LicenseStateMachine`, `LicenseLimits`, `DefaultTierLimits`) under package `com.cameleer.license`. No Spring or server-runtime deps; consumed by `cameleer-server-core` (validation/runtime gate) and `cameleer-license-minter` (vendor signing) — and transitively by `cameleer-saas` via the minter — without inheriting server internals. - `cameleer-server-core` — domain logic, storage interfaces, services (no Spring dependencies) - `cameleer-server-app` — Spring Boot web app, REST controllers, SSE, persistence, Docker orchestration +- `cameleer-license-minter` — vendor-only Ed25519 license signing library + CLI. Depends only on `cameleer-license-api` so consumers don't pull in `cameleer-server-core`. ## Build Commands diff --git a/cameleer-license-api/pom.xml b/cameleer-license-api/pom.xml new file mode 100644 index 00000000..b832c5e2 --- /dev/null +++ b/cameleer-license-api/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + + com.cameleer + cameleer-server-parent + 1.0-SNAPSHOT + + + cameleer-license-api + Cameleer License API + Pure license contract types — LicenseInfo, LicenseValidator, LicenseState, LicenseStateMachine, LicenseLimits, DefaultTierLimits. Shared by server-core (validation/runtime gate) and cameleer-license-minter (vendor-side signing). Has no Spring or server-runtime dependencies so consumers like cameleer-saas can depend on the minter without inheriting server internals. + + + + com.fasterxml.jackson.core + jackson-databind + + + org.slf4j + slf4j-api + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + none + + + + + + diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/DefaultTierLimits.java b/cameleer-license-api/src/main/java/com/cameleer/license/DefaultTierLimits.java similarity index 95% rename from cameleer-server-core/src/main/java/com/cameleer/server/core/license/DefaultTierLimits.java rename to cameleer-license-api/src/main/java/com/cameleer/license/DefaultTierLimits.java index 48b4c655..47ec3b9c 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/DefaultTierLimits.java +++ b/cameleer-license-api/src/main/java/com/cameleer/license/DefaultTierLimits.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import java.util.Collections; import java.util.LinkedHashMap; diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseInfo.java b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseInfo.java similarity index 97% rename from cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseInfo.java rename to cameleer-license-api/src/main/java/com/cameleer/license/LicenseInfo.java index d18354c8..5c6ef701 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseInfo.java +++ b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseInfo.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import java.time.Instant; import java.util.Map; diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseLimits.java b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseLimits.java similarity index 96% rename from cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseLimits.java rename to cameleer-license-api/src/main/java/com/cameleer/license/LicenseLimits.java index 48e29f18..550b5d44 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseLimits.java +++ b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseLimits.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import java.util.Collections; import java.util.LinkedHashMap; diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseState.java b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseState.java similarity index 68% rename from cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseState.java rename to cameleer-license-api/src/main/java/com/cameleer/license/LicenseState.java index 711c367a..05dc9557 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseState.java +++ b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseState.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; public enum LicenseState { ABSENT, diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseStateMachine.java b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseStateMachine.java similarity index 94% rename from cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseStateMachine.java rename to cameleer-license-api/src/main/java/com/cameleer/license/LicenseStateMachine.java index 12f12a3f..ad955ac0 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseStateMachine.java +++ b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseStateMachine.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; public final class LicenseStateMachine { diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseValidator.java b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseValidator.java similarity index 99% rename from cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseValidator.java rename to cameleer-license-api/src/main/java/com/cameleer/license/LicenseValidator.java index 09067ae4..ac7222f8 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseValidator.java +++ b/cameleer-license-api/src/main/java/com/cameleer/license/LicenseValidator.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/cameleer-server-core/src/test/java/com/cameleer/server/core/license/DefaultTierLimitsTest.java b/cameleer-license-api/src/test/java/com/cameleer/license/DefaultTierLimitsTest.java similarity index 96% rename from cameleer-server-core/src/test/java/com/cameleer/server/core/license/DefaultTierLimitsTest.java rename to cameleer-license-api/src/test/java/com/cameleer/license/DefaultTierLimitsTest.java index 398fe60b..2723d8b2 100644 --- a/cameleer-server-core/src/test/java/com/cameleer/server/core/license/DefaultTierLimitsTest.java +++ b/cameleer-license-api/src/test/java/com/cameleer/license/DefaultTierLimitsTest.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import org.junit.jupiter.api.Test; diff --git a/cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseInfoTest.java b/cameleer-license-api/src/test/java/com/cameleer/license/LicenseInfoTest.java similarity index 98% rename from cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseInfoTest.java rename to cameleer-license-api/src/test/java/com/cameleer/license/LicenseInfoTest.java index 22017e08..42b36e4b 100644 --- a/cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseInfoTest.java +++ b/cameleer-license-api/src/test/java/com/cameleer/license/LicenseInfoTest.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import org.junit.jupiter.api.Test; diff --git a/cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseStateMachineTest.java b/cameleer-license-api/src/test/java/com/cameleer/license/LicenseStateMachineTest.java similarity index 97% rename from cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseStateMachineTest.java rename to cameleer-license-api/src/test/java/com/cameleer/license/LicenseStateMachineTest.java index 09429499..4a61e91c 100644 --- a/cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseStateMachineTest.java +++ b/cameleer-license-api/src/test/java/com/cameleer/license/LicenseStateMachineTest.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import org.junit.jupiter.api.Test; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/core/license/LicenseValidatorTest.java b/cameleer-license-api/src/test/java/com/cameleer/license/LicenseValidatorTest.java similarity index 99% rename from cameleer-server-app/src/test/java/com/cameleer/server/core/license/LicenseValidatorTest.java rename to cameleer-license-api/src/test/java/com/cameleer/license/LicenseValidatorTest.java index 54e43835..581b665b 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/core/license/LicenseValidatorTest.java +++ b/cameleer-license-api/src/test/java/com/cameleer/license/LicenseValidatorTest.java @@ -1,4 +1,4 @@ -package com.cameleer.server.core.license; +package com.cameleer.license; import org.junit.jupiter.api.Test; diff --git a/cameleer-license-minter/README.md b/cameleer-license-minter/README.md index dea3ef50..9b025a9a 100644 --- a/cameleer-license-minter/README.md +++ b/cameleer-license-minter/README.md @@ -58,7 +58,7 @@ Two JARs land in `cameleer-license-minter/target/`: ```java import com.cameleer.license.minter.LicenseMinter; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; LicenseInfo info = new LicenseInfo( java.util.UUID.randomUUID(), @@ -136,11 +136,11 @@ base64(canonicalJson) + "." + base64(ed25519Signature) - The signature is computed with `Signature.getInstance("Ed25519")` over the canonical payload bytes (not over the base64-encoded form). - Encoding is `Base64.getEncoder()` (RFC 4648 §4 — *not* base64url). The validator decodes with the matching `Base64.getDecoder()`. -`LicenseValidator.validate(...)` (`cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseValidator.java:42`) splits on the first `.`, decodes both halves, verifies the signature, then deserializes the payload. +`LicenseValidator.validate(...)` (`cameleer-license-api/src/main/java/com/cameleer/license/LicenseValidator.java:42`) splits on the first `.`, decodes both halves, verifies the signature, then deserializes the payload. ## LicenseInfo schema -Source: `cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseInfo.java`. Field-by-field: +Source: `cameleer-license-api/src/main/java/com/cameleer/license/LicenseInfo.java`. Field-by-field: | Field | Type | Required | Semantics | |---|---|---|---| @@ -154,7 +154,7 @@ Source: `cameleer-server-core/src/main/java/com/cameleer/server/core/license/Lic ## Limits dictionary -Canonical key set: `cameleer-server-core/src/main/java/com/cameleer/server/core/license/DefaultTierLimits.java`. Any key not listed here is silently ignored by the server's `LicenseGate.getEffectiveLimits()`. +Canonical key set: `cameleer-license-api/src/main/java/com/cameleer/license/DefaultTierLimits.java`. Any key not listed here is silently ignored by the server's `LicenseGate.getEffectiveLimits()`. | CLI flag | Key | Default | What the server enforces | |---|---|---|---| @@ -284,4 +284,4 @@ mvn -pl cameleer-server-app dependency:tree | grep license-minter # expected: empty output (or, in development branches, a single line scoped 'test') ``` -`cameleer-license-minter/pom.xml` depends on `cameleer-server-core` for `LicenseInfo` and the validator round-trip used by `--verify`. The server app intentionally does not depend on the minter — vendors mint outside the customer-deployed runtime, and a compromised customer cannot leverage server code to forge tokens. +`cameleer-license-minter/pom.xml` depends on `cameleer-license-api` for the pure license contract types (`LicenseInfo`, `LicenseValidator`) used by mint + `--verify`. It deliberately does **not** depend on `cameleer-server-core`, so consumers of the minter (e.g. `cameleer-saas`) do not inherit server-runtime types onto their classpath. The server app intentionally does not depend on the minter — vendors mint outside the customer-deployed runtime, and a compromised customer cannot leverage server code to forge tokens. diff --git a/cameleer-license-minter/pom.xml b/cameleer-license-minter/pom.xml index 354c8ec2..dc58904c 100644 --- a/cameleer-license-minter/pom.xml +++ b/cameleer-license-minter/pom.xml @@ -17,7 +17,7 @@ com.cameleer - cameleer-server-core + cameleer-license-api com.fasterxml.jackson.core diff --git a/cameleer-license-minter/src/main/java/com/cameleer/license/minter/LicenseMinter.java b/cameleer-license-minter/src/main/java/com/cameleer/license/minter/LicenseMinter.java index 72c3caf8..c67107e3 100644 --- a/cameleer-license-minter/src/main/java/com/cameleer/license/minter/LicenseMinter.java +++ b/cameleer-license-minter/src/main/java/com/cameleer/license/minter/LicenseMinter.java @@ -1,6 +1,6 @@ package com.cameleer.license.minter; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/cameleer-license-minter/src/main/java/com/cameleer/license/minter/cli/LicenseMinterCli.java b/cameleer-license-minter/src/main/java/com/cameleer/license/minter/cli/LicenseMinterCli.java index 927cd5e7..b3c670f6 100644 --- a/cameleer-license-minter/src/main/java/com/cameleer/license/minter/cli/LicenseMinterCli.java +++ b/cameleer-license-minter/src/main/java/com/cameleer/license/minter/cli/LicenseMinterCli.java @@ -1,7 +1,7 @@ package com.cameleer.license.minter.cli; import com.cameleer.license.minter.LicenseMinter; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; import java.io.PrintStream; import java.nio.file.Files; @@ -107,7 +107,7 @@ public final class LicenseMinterCli { } try { String pubB64 = Files.readString(Path.of(pubPath)).trim(); - new com.cameleer.server.core.license.LicenseValidator(pubB64, tenant).validate(token); + new com.cameleer.license.LicenseValidator(pubB64, tenant).validate(token); out.println("verified ok"); } catch (Exception ve) { err.println("VERIFY FAILED: " + ve.getMessage()); diff --git a/cameleer-license-minter/src/test/java/com/cameleer/license/minter/LicenseMinterTest.java b/cameleer-license-minter/src/test/java/com/cameleer/license/minter/LicenseMinterTest.java index c2fddcd6..45465dd4 100644 --- a/cameleer-license-minter/src/test/java/com/cameleer/license/minter/LicenseMinterTest.java +++ b/cameleer-license-minter/src/test/java/com/cameleer/license/minter/LicenseMinterTest.java @@ -1,7 +1,7 @@ package com.cameleer.license.minter; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseValidator; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseValidator; import org.junit.jupiter.api.Test; import java.security.KeyPair; diff --git a/cameleer-license-minter/src/test/java/com/cameleer/license/minter/cli/LicenseMinterCliTest.java b/cameleer-license-minter/src/test/java/com/cameleer/license/minter/cli/LicenseMinterCliTest.java index 36f3dd2f..ec818ee5 100644 --- a/cameleer-license-minter/src/test/java/com/cameleer/license/minter/cli/LicenseMinterCliTest.java +++ b/cameleer-license-minter/src/test/java/com/cameleer/license/minter/cli/LicenseMinterCliTest.java @@ -1,6 +1,6 @@ package com.cameleer.license.minter.cli; -import com.cameleer.server.core.license.LicenseValidator; +import com.cameleer.license.LicenseValidator; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/config/LicenseBeanConfig.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/config/LicenseBeanConfig.java index 5d6bb834..877f1a7f 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/config/LicenseBeanConfig.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/config/LicenseBeanConfig.java @@ -4,8 +4,8 @@ import com.cameleer.server.app.license.LicenseRepository; import com.cameleer.server.app.license.LicenseService; import com.cameleer.server.core.admin.AuditService; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseValidator; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseValidator; import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/controller/LicenseAdminController.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/controller/LicenseAdminController.java index 3c5152c3..52cb1062 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/controller/LicenseAdminController.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/controller/LicenseAdminController.java @@ -3,7 +3,7 @@ package com.cameleer.server.app.controller; import com.cameleer.server.app.license.LicenseRepository; import com.cameleer.server.app.license.LicenseService; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.ResponseEntity; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseChangedEvent.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseChangedEvent.java index 59df6a7a..2dbc69c3 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseChangedEvent.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseChangedEvent.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; import java.util.Objects; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseEnforcer.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseEnforcer.java index 14cf71c4..f457d9f3 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseEnforcer.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseEnforcer.java @@ -3,8 +3,8 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.admin.AuditCategory; import com.cameleer.server.core.admin.AuditResult; import com.cameleer.server.core.admin.AuditService; +import com.cameleer.license.LicenseLimits; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseLimits; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseExceptionAdvice.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseExceptionAdvice.java index d994d678..8dffd781 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseExceptionAdvice.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseExceptionAdvice.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; +import com.cameleer.license.LicenseInfo; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMessageRenderer.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMessageRenderer.java index d1d9fbaa..03c31838 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMessageRenderer.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMessageRenderer.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; import java.time.Duration; import java.time.Instant; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMetrics.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMetrics.java index 2dbf5e28..3227cad0 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMetrics.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseMetrics.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseState; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.beans.factory.annotation.Value; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseService.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseService.java index 7d3509af..341888a3 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseService.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/LicenseService.java @@ -3,9 +3,9 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.admin.AuditCategory; import com.cameleer.server.core.admin.AuditResult; import com.cameleer.server.core.admin.AuditService; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseValidator; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/RetentionPolicyApplier.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/RetentionPolicyApplier.java index 33cd4afd..07153d50 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/license/RetentionPolicyApplier.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/license/RetentionPolicyApplier.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseLimits; +import com.cameleer.license.LicenseLimits; import com.cameleer.server.core.runtime.Environment; import com.cameleer.server.core.runtime.EnvironmentRepository; import org.slf4j.Logger; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java index 2cdaec9e..240cc356 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/TestSecurityHelper.java @@ -2,7 +2,7 @@ package com.cameleer.server.app; import com.cameleer.server.core.agent.AgentRegistryService; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; import com.cameleer.server.core.security.JwtService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AlertRuleCapEnforcementIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AlertRuleCapEnforcementIT.java index 3a203d48..579d48dd 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AlertRuleCapEnforcementIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AlertRuleCapEnforcementIT.java @@ -22,7 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Verifies that the {@code max_alert_rules} cap from the default tier is enforced at * {@code POST /api/v1/environments/{envSlug}/alerts/rules}. Default tier * {@code max_alert_rules = 2}; with no license installed the gate is in - * {@link com.cameleer.server.core.license.LicenseState#ABSENT} and the defaults are + * {@link com.cameleer.license.LicenseState#ABSENT} and the defaults are * authoritative. The first two creates succeed; the third must be rejected with the * structured 403 envelope produced by {@link LicenseExceptionAdvice}. */ diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AppCapEnforcementIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AppCapEnforcementIT.java index 46db41bd..0311bfc8 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AppCapEnforcementIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/AppCapEnforcementIT.java @@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * Verifies that the {@code max_apps} cap from the default tier is enforced at * {@code POST /api/v1/environments/{envSlug}/apps}. Default tier {@code max_apps = 3}; with no - * license installed the gate is in {@link com.cameleer.server.core.license.LicenseState#ABSENT} + * license installed the gate is in {@link com.cameleer.license.LicenseState#ABSENT} * and the defaults are authoritative. The fourth create attempt must be rejected with the * structured 403 envelope produced by {@link LicenseExceptionAdvice}. */ diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseEnforcerTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseEnforcerTest.java index db23264a..4f8460d1 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseEnforcerTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseEnforcerTest.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; import org.junit.jupiter.api.Test; import java.time.Instant; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseLifecycleIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseLifecycleIT.java index 2706a3e3..cf690118 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseLifecycleIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseLifecycleIT.java @@ -4,8 +4,8 @@ import com.cameleer.license.minter.LicenseMinter; import com.cameleer.server.app.AbstractPostgresIT; import com.cameleer.server.app.TestSecurityHelper; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMessageRendererTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMessageRendererTest.java index ec7961f9..ab2378ec 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMessageRendererTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMessageRendererTest.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; import org.junit.jupiter.api.Test; import java.time.Instant; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMetricsTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMetricsTest.java index 0ba28eb1..a39e54b4 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMetricsTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseMetricsTest.java @@ -1,7 +1,7 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; +import com.cameleer.license.LicenseInfo; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseServiceTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseServiceTest.java index 7f54da41..ae8656ae 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseServiceTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/LicenseServiceTest.java @@ -4,9 +4,9 @@ import com.cameleer.server.core.admin.AuditCategory; import com.cameleer.server.core.admin.AuditResult; import com.cameleer.server.core.admin.AuditService; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseState; -import com.cameleer.server.core.license.LicenseValidator; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; +import com.cameleer.license.LicenseValidator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationEventPublisher; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/OutboundCapEnforcementIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/OutboundCapEnforcementIT.java index 01994871..b3b8d4ac 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/OutboundCapEnforcementIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/OutboundCapEnforcementIT.java @@ -20,7 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Verifies that the {@code max_outbound_connections} cap from the default tier is enforced at * {@code POST /api/v1/admin/outbound-connections}. Default tier * {@code max_outbound_connections = 1}; with no license installed the gate is in - * {@link com.cameleer.server.core.license.LicenseState#ABSENT} and the defaults are + * {@link com.cameleer.license.LicenseState#ABSENT} and the defaults are * authoritative. The first create succeeds; the second must be rejected with the structured * 403 envelope produced by {@link LicenseExceptionAdvice}. */ diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionPolicyApplierTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionPolicyApplierTest.java index 566755a4..14c78a33 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionPolicyApplierTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionPolicyApplierTest.java @@ -1,8 +1,8 @@ package com.cameleer.server.app.license; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseInfo; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; import com.cameleer.server.core.runtime.Environment; import com.cameleer.server.core.runtime.EnvironmentRepository; import org.junit.jupiter.api.BeforeEach; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionRuntimeRecomputeIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionRuntimeRecomputeIT.java index 52d7062f..42d20817 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionRuntimeRecomputeIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/license/RetentionRuntimeRecomputeIT.java @@ -3,7 +3,7 @@ package com.cameleer.server.app.license; import com.cameleer.server.app.AbstractPostgresIT; import com.cameleer.server.app.TestSecurityHelper; import com.cameleer.server.core.license.LicenseGate; -import com.cameleer.server.core.license.LicenseState; +import com.cameleer.license.LicenseState; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/cameleer-server-core/pom.xml b/cameleer-server-core/pom.xml index cf0292ff..8ceb4eba 100644 --- a/cameleer-server-core/pom.xml +++ b/cameleer-server-core/pom.xml @@ -15,6 +15,10 @@ Domain logic, storage, and agent registry + + com.cameleer + cameleer-license-api + com.cameleer cameleer-common diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseGate.java b/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseGate.java index be6b2af3..250dd9cc 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseGate.java +++ b/cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseGate.java @@ -1,5 +1,9 @@ package com.cameleer.server.core.license; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseLimits; +import com.cameleer.license.LicenseState; +import com.cameleer.license.LicenseStateMachine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/core/license/LicenseGateTest.java b/cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseGateTest.java similarity index 94% rename from cameleer-server-app/src/test/java/com/cameleer/server/core/license/LicenseGateTest.java rename to cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseGateTest.java index 47804c4e..000c1ead 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/core/license/LicenseGateTest.java +++ b/cameleer-server-core/src/test/java/com/cameleer/server/core/license/LicenseGateTest.java @@ -1,5 +1,8 @@ package com.cameleer.server.core.license; +import com.cameleer.license.DefaultTierLimits; +import com.cameleer.license.LicenseInfo; +import com.cameleer.license.LicenseState; import org.junit.jupiter.api.Test; import java.time.Instant; diff --git a/pom.xml b/pom.xml index 06d1bfd4..03dbde7e 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ Observability server for Cameleer agents + cameleer-license-api cameleer-server-core cameleer-server-app cameleer-license-minter @@ -40,6 +41,11 @@ cameleer-common ${cameleer-common.version} + + com.cameleer + cameleer-license-api + ${project.version} + com.cameleer cameleer-server-core