EN · ÖZGÜN DEPO BELGESİ
Müşteri ve lisans işlemleri
Özgün İngilizce metin. Komutlar ve kanıtlar, belgede belirtilen revizyona ve ortama aittir.
product/licensing/go/CUSTOMER-OPERATIONS.md
Bu sayfada
Local customer/license operator workflow
This is a bounded operator workflow for the original Go entitlement backend. Customer IDs and manual order references are opaque ASCII identifiers, not user accounts or a PII directory. There is no public customer API, checkout, payment provider, email delivery or automated billing authorization. Operators must verify fulfillment and recovery evidence independently before supplying confirmation. A confirmation flag records operator intent; it does not prove payment. Original entitlements never replace upstream software distribution permissions.
Fulfillment and status
IssueForCustomer(customerID, orderID, expires, siteLimit, manualAuthorized)
requires explicit manual authorization and valid future expiry/site limits.
References contain 1..128 ASCII letters/digits with internal ., _, :, -;
the first character is alphanumeric. Use opaque identifiers, never email addresses.
Order references are globally unique. The first transaction creates a random
bearer key and atomically upgrades state to schema 2, preserving anonymous licenses.
Concurrent/repeated fulfillment for the same customer/order returns the existing
digest and created:false, without a bearer key. Another customer cannot reuse
that order. No duplicate license or plaintext bearer is stored.
Store the first returned key privately. A lost bearer cannot be restored by replaying fulfillment, and a digest is not an equivalent replacement credential. Confirmed lost-license replacement is available as a separate operator action; it never recovers or reissues the old bearer. A repeated order is not a renewal or an opportunity to change its plan. Anonymous existing licenses retain their previous operator workflow and are not silently attached to customers.
CustomerLicenseStatus(customerID,key) returns only opaque customer/order IDs,
expiry, site limit, revocation and active/approved counts. It reveals no bearer,
installation secret, domain list or signing material. Customer binding is checked
inside the same locked state transaction as all other operator mutations.
Renewal, revocation and installation recovery
RenewForCustomer requires a future expiry, does not shorten the current expiry,
and refuses a site limit below the number of active sites. It never reverses
revocation. RevokeForCustomer is customer-bound and idempotently revokes the
license, invalidating server-protected downloads through the existing verifier.
RecoverInstallationForCustomer(customerID,key,site,expectedOldDigest,newKey,confirm)
requires confirmed, independently verified operator recovery evidence, an approved
origin, a non-revoked/non-expired customer-bound license and an exact old-owner
SHA-256 digest. This compare-and-swap rejects stale recovery attempts. It changes
ownership to the replacement installation digest, marks it inactive, preserves
origin approval/site limits and invalidates old download tokens even after the
replacement is activated. There is no automatic migration or remote domain fetch.
The new WordPress installation must explicitly activate; the old owner is refused.
Prepare the replacement installation secret through the intended installation’s private/admin setup and deliver it privately to the operator. Installing it into WordPress, authenticating the human requester and proving domain ownership remain operator responsibilities; this is not a self-service account recovery portal. Already issued offline signed entitlements cannot learn server recovery instantly; their fixed validity/grace remains bounded, while downloads always recheck the current owner. Renewal may need a later explicit refresh after a signed denial; client anti-replay timestamps are not overridden.
Confirmed lost-license replacement
ReplaceLostLicenseForCustomer(customerID,oldOrderID,newOrderID,confirmReplacement)
looks up the original license using customer/order references without requiring a
lost secret. Confirmation must include independently verified customer identity
and authorization for every retained origin. The old license must be live and
not revoked, and the replacement order reference must be distinct and unused.
One locked transaction permanently revokes the old license and issues a brand-new
bearer under the new reference, preserving exactly its expiry, site limit and
approved origins. All replacement activations start empty. No new origins or
extra limits are granted; WordPress must explicitly save/activate its new license.
Old server download tokens remain invalid even after replacement activation.
A used replacement reference is rejected, including retries by the same customer; it never mints another key. Preserve the initial private output and investigate an uncertain operator result before attempting further recovery. The old revoked license cannot be renewed, recovered or replaced again. This is replacement, not plaintext-key restoration or reversal of revocation. Offline entitlement claims still have their fixed signed deadlines; package delivery checks the live revocation and ownership. There is no account reset, email, payment or public API.
Local CLI examples
Root integration wires these methods into the existing local binary. Use a private
state directory, server-only HMAC material through LICENSING_SIGNING_KEY, and a
restrictive umask. Do not pass bearer or installation secrets in process arguments.
/private/operator/key.json contains exactly {"license_key":"<private-key>"};
/private/operator/recovery.json contains exactly
{"license_key":"<private-key>","new_installation_key":"<private-replacement>"}.
These are examples of privately supplied credentials, not committed fixtures.
umask 077
original-licensing --database /private/operator/licenses.json fulfill \
--customer-id customer-001 --order-id verified-order-001 \
--expires <future-unix-time> --sites 2 --confirm-manual-fulfillment \
> /private/operator/issued-once.json
original-licensing --database /private/operator/licenses.json customer-status \
--customer-id customer-001 < /private/operator/key.json
original-licensing --database /private/operator/licenses.json customer-renew \
--customer-id customer-001 --expires <later-future-unix-time> --sites 2 \
< /private/operator/key.json
original-licensing --database /private/operator/licenses.json customer-revoke \
--customer-id customer-001 < /private/operator/key.json
original-licensing --database /private/operator/licenses.json customer-recover-installation \
--customer-id customer-001 --site https://approved.example \
--expected-old-digest <old-installation-sha256> --confirm-recovery \
< /private/operator/recovery.json
# Alternative lost-bearer workflow: no old secret is needed.
original-licensing --database /private/operator/licenses.json customer-replace-license \
--customer-id customer-001 --old-order-id verified-order-001 \
--new-order-id confirmed-replacement-001 --confirm-replacement \
> /private/operator/replacement-key-once.json
Recovery and revocation above are alternative lifecycle operations: a revoked license cannot subsequently recover or renew. Capture a valid old installation digest from independently verified private operator records, not an untrusted claim. Service deployment, customer identity verification, billing and original artifact distribution authorization remain separate release gates.
Evidence
Focused tests cover manual-authorization refusal without a key/state write, concurrent eight-way fulfillment with exactly one bearer issuance, idempotent replays, cross-customer rejection, safe counts, renewal limits, revocation, unchanged snapshots after failures, legacy retention under schema 2, recovery CAS, expiry/revocation recovery refusal, inactive replacement, confirmed lost-bearer replacement without the old secret, used-order rejection without partial revocation, and old tokens rejected before and after replacement activation. Tests use temporary synthetic licenses, keys and an original fixture ZIP; no customer data or production secrets.
go test ./... -run '^TestCustomer' -count=1
go test -race ./... -run '^TestCustomerConcurrentFulfillmentOneKey$' -count=1
State schema 2 deliberately rejects older schema-1-only server versions so they cannot silently strip customer/order references. Backups must be restored only with a compatible backend and the existing ambiguity/revocation restore controls.