EN · ÖZGÜN DEPO BELGESİ
Go servisi ve komut satırı
Özgün İngilizce metin. Komutlar ve kanıtlar, belgede belirtilen revizyona ve ortama aittir.
product/licensing/go/README.md
Bu sayfada
Go licensing backend
This is the runnable backend for the independently licensed original product.
../service.py remains a test/reference prototype; it is not required at runtime.
The cache core has no dependency on either service. This directory uses only the
Go standard library and preserves the PHP adapter’s routes and signed claims.
Build on Linux with Go 1.24 or newer (Astro/admin extension tested with Go 1.27.1):
cd product/licensing/go
umask 077
go build -o /tmp/original-licensing .
go test -race ./...
go vet ./...
go build -buildvcs=false is useful when building an exported checkout or a
worktree whose Git metadata is unavailable to the build process. A static Linux
binary can be built with CGO_ENABLED=0. No Python, OpenSSL executable, SQLite,
or external Go module is required to run the binary. Generate a temporary RSA
key separately for local fixtures (openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048; export with openssl pkey -pubout). Private PEMs may be
PKCS#1 or PKCS#8; RSA keys must be at least 2048 bits.
Local operator commands
Supply LICENSING_SIGNING_KEY using at least 32 bytes of random, server-only
HMAC material. Set it privately; never commit it or deliver it to PHP. The CLI
requires --database before its subcommand. Use a fresh Go JSON state path:
Python SQLite files are rejected, and ownership is never silently migrated.
/tmp/original-licensing --database /tmp/original-licenses.json issue --expires <future-unix-time> --sites 1
# Feed the issued key on stdin, after independently verifying origin ownership:
/tmp/original-licensing --database /tmp/original-licenses.json approve-origin --key-stdin --site https://fixture.example
/tmp/original-licensing --database /tmp/original-licenses.json serve --port 8090 --private-key /tmp/private.pem --original-package /tmp/original-fixture.zip --version 0.1.0
# Revoke using an independent operator process while the server is running:
/tmp/original-licensing --database /tmp/original-licenses.json revoke --key-stdin
The listener is fixed to 127.0.0.1. The Go process never fetches supplied origins
or external packages. ZIPs must be explicitly supplied original artifacts with a
semantic version; fixtures contain synthetic original text only. No upstream
package is downloaded or distributed by this backend. Private credentials,
billing, public deployment and TLS termination remain unconfigured. The optional
Astro portal includes authenticated operator administration with an explicit
startup allowlist; see PORTAL.md. The cache and PHP licensing APIs
retain their existing behavior.
API and persistence
Every POST requires precisely license_key, installation_key, site as JSON
strings. Existing /activate, /deactivate, /check, /entitlement, /updates,
/download-token and bearer-authenticated GET /download are preserved. Requests
are bounded to 16 KiB; duplicate/unknown fields, null values and trailing JSON
are rejected. All replies use Cache-Control: no-store; bearer identities are
never access-logged. HTTP server timeouts and header bounds are explicit.
Origins are approved per license before activation. Hosts normalize to lower case, default HTTPS port disappears, and paths/credentials/query/fragments are rejected. Use ASCII/punycode hosts: the existing PHP client does not perform IDNA conversion, so Unicode origins are explicitly rejected rather than bound to an origin the PHP client cannot reproduce. DNS labels and ports are validated.
The first activation binds the origin to a SHA-256 installation digest. Same
owner reactivation is idempotent; deactivation frees the site slot while retaining
ownership. Revoked/expired licenses cannot activate, check or get updates. Bound
identities receive signed inactive/revoked/expired denials; unapproved origins,
unknown licenses and wrong owners receive unsigned 403. Signer/storage failures
receive unsigned 503. RSA PKCS#1 v1.5 SHA-256 signs the decoded compact sorted
JSON bytes; envelope fields remain unpadded Base64URL payload, sig, and
kid selected through SetSigningKeyID (default "fixture-v1"). Timestamps remain integer seconds, with refresh/validity/grace
windows capped at 300/3600/3600 seconds and the underlying license expiry.
Update metadata includes version, ZIP SHA-256 and an HMAC download token lasting at most 300 seconds and never beyond license expiry. The configured ZIP is read and validated once, then served as immutable startup bytes. A token must match that ZIP’s digest. Each download reloads live license, ownership, approval and activation state: operator revocation or HTTP deactivation invalidates previously issued tokens immediately for subsequent checks.
State consists only of schema version, license hashes, deadlines, limits,
approved origins and installation hashes/activation state. Every operation uses
an independent flock descriptor and reloads state while holding the lock, so
separate operator processes and concurrent HTTP requests observe one state.
Writes use a mode-0600 temporary file, file fsync, atomic rename and parent-directory
fsync. Failed transaction callbacks publish no changes. The CLI uses umask 077;
new state directories use 0700. Schema corruption fails closed. Limits are
10,000 licenses, 100,000 approved origins, 16 MiB persisted JSON and 64 MiB ZIP.
The site limit is 1..1000. This is intentionally a small local backend: state
reads and mutations serialize, and whole-state rewrites trade simplicity for
limited capacity. Use a local filesystem with working flock/rename/fsync; shared
network filesystems are unsupported. A storage error after rename can report
failure despite a committed snapshot, so retries must account for that ambiguity.
Evidence and remaining gates
Focused Go tests cover canonical signature bytes and integer JSON timestamps, signed denial states, expiry-capped grace, 16 concurrent stores competing for one slot, retained ownership, live cross-instance revocation, restart readback, mode-0600 state, aborted-transaction rollback, corruption, bounded identities, token tampering and immutable ZIP bytes. HTTP tests preserve routes/status codes, strict JSON and unsigned signer outage behavior.
The opt-in real HTTP PHP test starts a loopback Go handler and runs the existing PHP adapter in an already installed PHP Docker image. It creates temporary keys, state and a synthetic ZIP, checks activation, PHP RSA verification, metadata, byte-identical download, offline grace/expiry and live signed revocation/old-token rejection, then removes its containers and temporary fixture files:
LICENSING_PHP_DOCKER_IMAGE=<existing-php-image> go test -race -v ./...
That fixture uses Docker host networking (Linux) and creates two short-lived CLI
containers; normal go test skips it. It does not prove production TLS, billing,
admin authorization, disaster recovery or service capacity. Existing WordPress
admin tests and a separate actual WordPress/Go integration fixture establish the
admin behavior.
Signing rotation and conservative reconciliation now
provide explicit signer IDs and a recovery step using an operator-acknowledged
authoritative snapshot. Operator ownership evidence/recovery, migration tooling,
key custody/distribution, authority freshness, backups/auditing, authenticated
issuance/renewals/refunds and a public HTTPS
service remain operational gates.