From 521f884b85863baccd9702c563d3fd4e96092bb5 Mon Sep 17 00:00:00 2001 From: server Date: Tue, 14 Apr 2026 12:34:07 +0200 Subject: [PATCH] Add operational docs for release workflow --- README.md | 3 ++ docs/key-rotation.md | 72 +++++++++++++++++++++++++++++++++ docs/migration.md | 65 ++++++++++++++++++++++++++++++ docs/release-workflow.md | 86 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 docs/key-rotation.md create mode 100644 docs/migration.md create mode 100644 docs/release-workflow.md diff --git a/README.md b/README.md index 852f673..c12b8a5 100644 --- a/README.md +++ b/README.md @@ -187,3 +187,6 @@ See [docs/format.md](docs/format.md) and [docs/client-integration.md](docs/client-integration.md). For Codex and Claude Code MCP setup, see [docs/agent-setup.md](docs/agent-setup.md). For the runtime key payload contract, see [docs/launcher-contract.md](docs/launcher-contract.md). +For release steps, see [docs/release-workflow.md](docs/release-workflow.md). +For key rotation policy, see [docs/key-rotation.md](docs/key-rotation.md). +For legacy pack migration, see [docs/migration.md](docs/migration.md). diff --git a/docs/key-rotation.md b/docs/key-rotation.md new file mode 100644 index 0000000..a73c1f6 --- /dev/null +++ b/docs/key-rotation.md @@ -0,0 +1,72 @@ +# Key rotation + +`m2pack-secure` supports archive-level `key_id` so releases can move away from a +single permanent content key. + +## Why rotate + +Rotate when: + +- a content key may have leaked +- a signing key may have leaked +- you want to expire old launcher material +- you want to separate major release lines +- you change pack validation or loader policy + +## Current model + +- each archive stores a `key_id` in its header +- the client chooses the verifier public key by `key_id` +- the runtime master key must be delivered with the same `key_id` +- `.m2p` loading fails if the archive `key_id` and runtime `key_id` do not match + +## Recommended policy + +- keep `key_id` monotonic +- reserve one `key_id` per release line or rotation event +- never reuse an old `key_id` for different secret material +- keep old public verification keys only as long as you need to support those releases +- remove stale keys from the client once old archives are no longer supported + +## Rotation procedure + +1. Generate a new `master.key`. +2. Generate a new signing keypair if needed. +3. Assign a new `key_id`. +4. Rebuild archives with `--key-id `. +5. Export a new `M2PackKeys.h` for the client. +6. Export a new runtime key payload for the launcher. +7. Ship client, launcher, and `.m2p` together. +8. Retire old runtime material after rollout is complete. + +## Example + +```bash +./build/m2pack keygen --out-dir keys-2026-05 + +./build/m2pack build \ + --input /srv/build/client-root \ + --output out/root.m2p \ + --key keys-2026-05/master.key \ + --sign-secret-key keys-2026-05/signing.key \ + --key-id 4 + +./build/m2pack export-client-config \ + --key keys-2026-05/master.key \ + --public-key keys-2026-05/signing.pub \ + --key-id 4 \ + --output /path/to/m2dev-client-src/src/PackLib/M2PackKeys.h + +./build/m2pack export-runtime-key \ + --key keys-2026-05/master.key \ + --public-key keys-2026-05/signing.pub \ + --key-id 4 \ + --format blob \ + --output out/runtime-key.bin +``` + +## Operational note + +If you need overlapping support for multiple release lines, extend the client +header with multiple verifier slots and keep launcher delivery strict: one +runtime master key per active process, matching the archive `key_id`. diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 0000000..62afd24 --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,65 @@ +# Migration from legacy packs + +This project is meant to replace legacy `.pck` or EterPack-style content +delivery gradually, not through a single risky cutover. + +## Target state + +- Linux builds produce `.m2p` +- the client loads `.m2p` first +- runtime master key delivery is required +- silent fallback to legacy packs is disabled in production + +## Suggested migration phases + +### Phase 1: parallel loader + +- keep the legacy loader in the client +- add `.m2p` support beside it +- verify the new loader against real content sets +- keep fallback only in development if needed + +### Phase 2: pipeline validation + +- build `.m2p` from the same source tree as legacy packs +- run `verify` for every archive +- run `diff` between the source tree and `.m2p` +- compare in-game behavior between legacy and `.m2p` + +### Phase 3: runtime delivery + +- stop embedding a real content key in the client +- deliver the runtime key through launcher, environment, or mapping +- verify startup failure behavior when the runtime key is missing + +### Phase 4: production cutover + +- package only `.m2p` for release clients +- disable silent fallback to legacy packs +- remove stale pack generation from release CI +- keep extract/debug tools only for internal use + +## Recommended checks + +- duplicate normalized paths +- invalid relative paths +- case-collision problems on Windows +- decompression failures +- manifest signature failures +- key mismatch by `key_id` + +## Practical migration loop + +1. Pick one legacy pack group. +2. Rebuild it as `.m2p`. +3. Verify it. +4. Diff it against the source tree. +5. Boot the client with runtime keys. +6. Validate asset loads in logs and in-game. +7. Move to the next pack group. + +## Risk notes + +- Do not mix silent fallback with production security claims. +- Do not ship a client that accepts `.m2p` without runtime key enforcement. +- Do not keep real secret keys inside the repository. diff --git a/docs/release-workflow.md b/docs/release-workflow.md new file mode 100644 index 0000000..988463e --- /dev/null +++ b/docs/release-workflow.md @@ -0,0 +1,86 @@ +# Release workflow + +This is the recommended end-to-end release flow for `m2pack-secure`. + +The goal is to keep archive creation on Linux, keep signing keys off developer +workstations when possible, and make the Windows client consume only runtime +delivery material. + +## Inputs + +- client asset tree +- `master.key` +- `signing.key` +- `signing.pub` +- target `key_id` + +## Output artifacts + +- one or more `.m2p` archives +- generated `M2PackKeys.h` +- runtime key payload for the launcher +- release manifest and validation logs + +## Minimal release flow + +1. Generate or select the active content key set. +2. Build the archive with the intended `key_id`. +3. Verify the archive with the signing public key and content key. +4. Diff the archive against the source tree to catch packing mistakes. +5. Export `M2PackKeys.h` for the client source tree. +6. Export the runtime key payload for the launcher. +7. Build the Windows client against the generated header. +8. Ship `.m2p` plus the client and launcher together. + +## Example + +```bash +./build/m2pack build \ + --input /srv/build/client-root \ + --output out/root.m2p \ + --key keys/master.key \ + --sign-secret-key keys/signing.key \ + --key-id 3 \ + --json + +./build/m2pack verify \ + --archive out/root.m2p \ + --public-key keys/signing.pub \ + --key keys/master.key \ + --json + +./build/m2pack diff \ + --left /srv/build/client-root \ + --right out/root.m2p \ + --json + +./build/m2pack export-client-config \ + --key keys/master.key \ + --public-key keys/signing.pub \ + --key-id 3 \ + --output /path/to/m2dev-client-src/src/PackLib/M2PackKeys.h \ + --json + +./build/m2pack export-runtime-key \ + --key keys/master.key \ + --public-key keys/signing.pub \ + --key-id 3 \ + --format json \ + --output out/runtime-key.json \ + --json +``` + +## CI recommendations + +- Run archive builds on Linux only. +- Keep `signing.key` in CI secrets or on a dedicated release box. +- Treat `master.key` as release secret material, not as source code. +- Archive and retain `verify` and `diff` outputs for each release. +- Fail the pipeline if `verify` fails or `diff` reports unexpected changes. + +## Production posture + +- The client should embed only verifier material and metadata. +- The launcher should provide the runtime master key. +- If `.m2p` exists and runtime key delivery fails, startup should fail hard. +- Do not silently fall back to legacy packs in production.