Skip to content

Commit 9dbf7e8

Browse files
chore(license): move owlicgen out of the product; add dev entitlement bypass (#702)
* chore(license): remove owlicgen from the product; add dev entitlement bypass owlicgen mints/signs license JWTs — that is Hanalyx-as-issuer infrastructure, not part of the product customers run (and the repo is heading to open source). Remove cmd/owlicgen; it is preserved as issuer reference under ~/hanalyx/OWAR/licensing/ with an implementation guide for the hanalyx.com dashboard team. The product keeps only license *verification* (internal/license), never minting. Nothing automated depended on owlicgen (tests mint JWTs via their own fixtures). To keep local paid-feature testing working without minting a license, add a build-tag-gated dev entitlement bypass: - entitlements_release.go (//go:build !dev): devEntitlementsEnabled()=false — physically absent from release binaries; IsEnabled behaves normally. - entitlements_dev.go (//go:build dev): unlocks features only when OPENWATCH_DEV_MODE=true. Two gates (build tag AND env), so a release binary can never unlock paid features from the environment alone. - IsEnabled short-circuits on the bypass. - scripts/openwatch.sh builds with -tags dev and sets OPENWATCH_DEV_MODE=true. - Tests both ways: release build asserts the bypass is OFF even with the env set (release-safety guard); -tags dev build asserts it gates on the env var. Also reword the api-license AC and the LoadJWT comment that name-dropped owlicgen. * chore(license): dev entitlement bypass + drop owlicgen prose refs Completes the owlicgen removal (the deletion landed in the previous commit): - build-tag-gated dev entitlement bypass (entitlements_{release,dev}.go) wired into IsEnabled; OFF in release builds, ON only under -tags dev with OPENWATCH_DEV_MODE=true. Tests assert both directions. - scripts/openwatch.sh builds -tags dev and sets OPENWATCH_DEV_MODE=true so local dev keeps paid-feature access without minting a license. - reword api-license AC-10 and the LoadJWT comment that referenced owlicgen.
1 parent 38828f6 commit 9dbf7e8

9 files changed

Lines changed: 76 additions & 132 deletions

File tree

cmd/owlicgen/main.go

Lines changed: 0 additions & 129 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build dev
2+
3+
package license
4+
5+
import "os"
6+
7+
// devEntitlementsEnabled reports whether the local-dev entitlement bypass is
8+
// active. This variant is compiled ONLY into `-tags dev` builds (never
9+
// released), and even then unlocks paid features only when OPENWATCH_DEV_MODE=true.
10+
// Two independent gates — the build tag AND the env var — so paid features can be
11+
// exercised locally (e.g. via scripts/openwatch.sh) without minting a license,
12+
// while a release binary contains none of this code. Replaces the local use of
13+
// the removed owlicgen tool.
14+
func devEntitlementsEnabled() bool { return os.Getenv("OPENWATCH_DEV_MODE") == "true" }
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//go:build dev
2+
3+
package license
4+
5+
import "testing"
6+
7+
// TestDevEntitlements_OnUnderDevTag verifies the local-dev bypass behaves as
8+
// documented in `-tags dev` builds: gated on OPENWATCH_DEV_MODE, off by default,
9+
// on when set. Compiled only under -tags dev (the launcher's build mode).
10+
func TestDevEntitlements_OnUnderDevTag(t *testing.T) {
11+
t.Setenv("OPENWATCH_DEV_MODE", "")
12+
if devEntitlementsEnabled() {
13+
t.Fatal("bypass should be off when OPENWATCH_DEV_MODE is unset, even under -tags dev")
14+
}
15+
t.Setenv("OPENWATCH_DEV_MODE", "true")
16+
if !devEntitlementsEnabled() {
17+
t.Fatal("bypass should be on under -tags dev with OPENWATCH_DEV_MODE=true")
18+
}
19+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build !dev
2+
3+
package license
4+
5+
// devEntitlementsEnabled reports whether the local-dev entitlement bypass is
6+
// active. In release builds (the default — no `dev` build tag) it is always
7+
// false: the bypass is physically absent from shipped binaries, so a production
8+
// install can never unlock paid features without a real license, regardless of
9+
// environment. The dev variant lives in entitlements_dev.go (built only under
10+
// `-tags dev`).
11+
func devEntitlementsEnabled() bool { return false }
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build !dev
2+
3+
package license
4+
5+
import "testing"
6+
7+
// TestDevEntitlements_OffInReleaseBuilds is a release-safety guard. The default
8+
// build carries no `dev` tag, so the dev entitlement bypass MUST be off even
9+
// with OPENWATCH_DEV_MODE set — otherwise a release binary could unlock paid
10+
// features from the environment alone. This runs in normal CI (no -tags dev) and
11+
// fails if the release variant ever starts returning true.
12+
func TestDevEntitlements_OffInReleaseBuilds(t *testing.T) {
13+
t.Setenv("OPENWATCH_DEV_MODE", "true")
14+
if devEntitlementsEnabled() {
15+
t.Fatal("dev entitlement bypass is active in a non-dev build; it must be physically absent from release binaries")
16+
}
17+
}

internal/license/service.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ func LoadFile(path string, opts VerifyOptions) (VerifyResult, error) {
6565
}
6666

6767
// LoadJWT validates a raw JWT string and swaps it into runtime state on
68-
// success. Useful for tests and for owlicgen-driven local installs.
68+
// success. Useful for tests and for installing a license JWT minted by the
69+
// Hanalyx issuer (the product only verifies licenses; it never mints them).
6970
func LoadJWT(jwtBlob string, opts VerifyOptions) (VerifyResult, error) {
7071
if err := Init(); err != nil {
7172
return VerifyMalformedJWT, err

internal/license/state.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ var (
2222
//
2323
// p99 < 50ns; no allocation. Spec AC-8, AC-9.
2424
func IsEnabled(f Feature) bool {
25+
// Local-dev bypass: only ever true in `-tags dev` builds with
26+
// OPENWATCH_DEV_MODE=true (see entitlements_{dev,release}.go). Always false
27+
// in release binaries — the bypass code is not compiled in.
28+
if devEntitlementsEnabled() {
29+
return true
30+
}
2531
s := current.Load()
2632
return s.IsEnabled(f)
2733
}

scripts/openwatch.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,11 @@ backend_start() {
182182
ow_version="$( . "$ROOT/packaging/version.env" >/dev/null 2>&1; echo "${VERSION:-dev}" )"
183183
ow_commit="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)"
184184
ow_built="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
185-
go build -ldflags "\
185+
# -tags dev compiles the local-dev entitlement bypass (entitlements_dev.go) so
186+
# paid features can be exercised locally without a license. The bypass is
187+
# physically absent from release builds (the Makefile does not pass this tag)
188+
# and still requires OPENWATCH_DEV_MODE=true at runtime (set below).
189+
go build -tags dev -ldflags "\
186190
-X github.com/Hanalyx/openwatch/internal/version.Version=${ow_version} \
187191
-X github.com/Hanalyx/openwatch/internal/version.Commit=${ow_commit} \
188192
-X github.com/Hanalyx/openwatch/internal/version.BuildTime=${ow_built}" \
@@ -193,6 +197,7 @@ backend_start() {
193197
OPENWATCH_SERVER_TLS_KEY="$TLS_DIR/key.pem" \
194198
OPENWATCH_IDENTITY_JWT_PRIVATE_KEY="$JWT_KEY" \
195199
OPENWATCH_IDENTITY_CREDENTIAL_KEY_FILE="$CRED_KEY" \
200+
OPENWATCH_DEV_MODE="true" \
196201
nohup "$BIN" serve >"$BE_LOG" 2>&1 &
197202
echo $! > "$BE_PID"
198203
poll_health "https://${OPENWATCH_SERVER_LISTEN}/api/v1/health" "$BE_PID" "$BE_LOG" "backend" 1

specs/api/license.spec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,5 @@ spec:
8484
description: POST /:premium-echo with license that includes premium_diagnostics returns 200 with echoed message.
8585
priority: critical
8686
- id: AC-10
87-
description: Installing license (owlicgen + file drop + SIGHUP) makes GET /license reflect new state without restart.
87+
description: Installing license (file drop + SIGHUP) makes GET /license reflect new state without restart.
8888
priority: high

0 commit comments

Comments
 (0)