OwnerService/HostService exchange only SessionOffer, PortableSessionStarted, PortableCheckpoint and
OwnerAck. Every checkpoint is merge3(C0, B0, current B1); sequences are monotonic and idempotent;
stale/superseded/malformed/oversized messages change nothing. Design and login-path audit in
docs/PORTABLE_SESSIONS.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Live stages drive real worldservers on both realms (A -> B -> play -> reconcile -> A -> B) with
independent expectations; portable_smoke example for the manual real-client test;
docs/PORTABLE_RECONCILE.md; reset script for the disposable realms.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Imports a canonical snapshot into a stopped disposable realm through the existing
Db abstraction, in one MySQL transaction: guids allocated inside the transaction
from the realm's own tables and every reference remapped, orphan cleanup, name
and start-position handling, self-asserting result, and a marker row. Untrusted
strings and blobs reach SQL only as hex literals from a typed encoder.
Manager mappings are recorded only after the realm commit, in one local
transaction; an import journal (schema 3) plus the marker recover a crash
between the two commits. Preflight refuses wrong ruleset, running realm,
missing account/content and more before anything is written.
Stock character_talent rows are never written: a real worldserver asserts on a
non-talent row. Item identity no longer includes the crafter (v2).
Production import remains the designed in-core PortableImportService
(docs/PORTABLE_IMPORT_SERVICE.md); nothing of it is implemented.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
character_settings policy v2: the carry list is ten strict key patterns
(core.ascension_active_spec, _starter, _starter_live, _reset_credits,
_slot.active, _slot.#, _slot.#.build.#, _slot.#.bar.#, _build.#, _bar.#),
matched exactly, not by prefix. Spell charges, Destiny Weaver, dynamic XP,
Runemaster, prestige/glory, tutorial flags, Wildcard state and anything unknown
are quarantined in the opaque extension and never applied. The importer's own
marker source (coa.portable.) is dropped.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reads one offline character out of a realm's characters database through the
existing Db abstraction: fixed SQL with the local guid as the only (numeric)
input, one consistent READ ONLY snapshot per character, hex-encoded text.
Maps the approved minimal v1 scope to the portable model; refuses online,
deleted, bot, active challenge/game mode/Manastorm and unknown-table cases.
Adds the 163-table registry, the settings policy, "Make Portable" in one store
transaction, and item mapping lifecycle with content identity (schema 2) so a
recycled local item guid never inherits an old portable item.
Recorded answers of a real MySQL drive the unit tests; live tests re-verify
them, read-only enforcement and snapshot consistency on a disposable server.
No realm database is written and nothing changes for existing installs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds crates/coa-core/src/portable: UUIDv7 identities, namespaced content ids,
the v1 portable character model (validated, bounded, canonical), compressed
hashed snapshots, a union-only collection set, and a SQLite (WAL) store with
revisions, stale-revision rejection, rollback-as-new-revision, configurable
history, realm/item mappings and forward-only migrations.
No realm database, network or UI is touched; no existing behaviour changes.
45 new tests. Format documented in docs/PORTABLE_FORMAT.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Audit of the real characters schema (163 tables), save/load paths, CoA-specific
state (character_settings, wardrobe/vanity collections), id domains to remap,
running-realm hazards, Manager integration points, risks and open decisions.
Includes the exact proposed plan for Phase 1 (local canonical storage).
No production code.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Record how the OVHcloud VPS used for the future registry/relay was set up:
hardening, firewall policy, Docker layout, health check, recovery path.
No IP addresses, keys or credentials are stored.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Db runs mysql, mysqldump and mysqladmin inside the database container with
docker exec -i when the server folder is a Docker installation, connecting over
TCP on the container's loopback like the repack's commands do on the host. The
password still travels only in MYSQL_PWD. Every caller keeps using Db unchanged
(accounts, backups, population, migrations, the RA service account), and the
host needs no MySQL client. The repack path is untouched: one line before its
tools are looked up. A stopped container or a missing one is reported as a
database that is not running.
Tested: 4 new tests (commands built for the container, no password on a command
line, repack unchanged, error classification), and a real run against the
Docker folder: SQL queries, a quick backup (156 characters tables), restore with
the previous database kept, a damaged backup refused, the RA service account
created by SQL, the world server console reached through its published
loopback port, an account created through it and listed by the Manager.
A server folder with Settings/docker.json is a Docker installation: MySQL, the
auth server and the world server run in containers controlled through the
docker command line. Every other folder is a repack and goes through the
unchanged Windows code. driver::run, process::observe and layout::scan check
for the marker first and hand over to coa_core::docker.
- start: network, runtime image (libraries only, binaries are mounted from
Core/), database healthy, world listening, then auth. A container that stops
while starting reports the cause found in its log (DatabaseNotRunning,
PortInUse, ...). New error code DockerUnavailable (docker missing, stopped,
or not usable by this user).
- stop: world (180 s to save), auth, database; exit code 0 observed.
- observe: container state, health and port readiness mapped to the existing
ServiceState. Readiness is a connection that stays open, because Docker's
proxy accepts connections before the server inside listens.
- passwords go by environment, never on a command line; the remote console is
published on 127.0.0.1 only; the server's own database updater is turned off
(it needs a mysql client the image does not have and stops the server).
Tested: 18 new tests with an in-memory Docker, and a real run through
examples/drive: database, world (ready in 11 s) and auth started from a
package-shaped folder, stopped with exit code 0 and restarted.
Not included: installation, database setup, backups and SQL through the
container, updates, firewall/exposure, the client under Wine/Proton.
PLAY turns into UPDATE CLIENT when a tracked client is outdated; with no client linked
it offers choosing an existing folder or downloading from the community launcher servers
(resumable, hash-verified, free-space checked, progress and stop). Files the player changed
are kept on request or moved aside, never silently replaced. Existing clients can be adopted
from Settings. Fresh downloads get a realmlist for each installed locale.
Fixes zero-length files in the downloader (found by the live probe).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>