MySQL implicitly commits around every ALTER TABLE, so the transaction
wrapping the reshape is real only on sqlite and postgres. On MySQL an
interrupted run could stop between the two legacy column drops, and the
rerun keyed its already-migrated check on the cluster column alone --
leaving the subdomain column behind forever.
Key the entry check on either legacy column remaining, run the backfill
and its loud-failure check only while the cluster column exists (they
provably completed before any drop), and skip drops for columns already
gone. No data-loss window existed in any state: the backfill commits
before the first drop on every engine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the settings row's (cluster, subdomain) identity columns with
(domain, proxy_address): domain is the endpoint hostname agents call,
proxy_address the declared cluster address of the proxy serving it. The
serving shape is the shape of the pin -- self-addressed (domain ==
proxy_address, a proxy dedicated to the account) or labeled (domain one
label beneath a shared cluster's address) -- so no mode flag or config
exists anywhere, and any mix of shapes coexists per account on one
deployment.
Bootstrap becomes an explicit POST /api/agent-network/settings taking
exactly one of proxy_address (the server allocates an adjective-noun
label beneath it) or endpoint (claimed verbatim, address-first). The
identity fields leave the PUT schema entirely -- immutability by shape
rather than by runtime rejection -- and provider create loses
bootstrap_cluster and all settings side effects, which also retires two
latent bugs: the dashboard's unsorted [0] free-domain pick making a
permanent decision, and bootstrap failures swallowed at debug level
inside a 200 provider create.
The global unique index moves from the label to the full hostname --
the actual invariant. Labels may repeat across clusters again, and
self-addressed rows (no label) cannot collide on an empty string. The
reverse lookup becomes a point query on that index, deleting the
clusterFromDomain suffix heuristic, and the synthesizer stamps
ProxyCluster from the settings row as a field read. A pre-AutoMigrate
migration backfills existing rows (domain = subdomain.cluster,
proxy_address = cluster) and drops the legacy columns, failing loudly
on rows with no identity to derive.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* [management] Ensure SessionStartedAt has a default value
Avoid null values for the new column
* [management] Add PeerStatus with LastSeen in peer_test
* [management] Add migration for PeerStatusSessionStartedAt default value
* [management] Add PeerStatus with LastSeen in migration tests
* Fix store migration on empty string
when fetching empty values from the database to check for migration our parser failed to handle null strings preventing the service from start
this uses sql.NullString to handle that and check for empty string resulted from null data
---------
Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
* serialize net.IP as json
* migrate net ip field from blob to json
* run net ip migration
* remove duplicate index
* Refactor
* Add tests
* fix tests
* migrate null blob values
This PR simplifies Store and FileStore
by keeping just the Get and Save account methods.
The AccountManager operates mostly around
a single account, so it makes sense to fetch
the whole account object from the store.
* feature: add User entity to Account
* test: new file store creation test
* test: add FileStore persist-restore tests
* test: add GetOrCreateAccountByUser Accountmanager test
* refactor: rename account manager users file
* refactor: use userId instead of accountId when handling Management HTTP API
* fix: new account creation for every request
* fix: golint
* chore: add account creator to Account Entity to identify who created the account.
* chore: use xid ID generator for account IDs
* fix: test failures
* test: check that CreatedBy is stored when account is stored
* chore: add account copy method
* test: remove test for non existent GetOrCreateAccount func
* chore: add accounts conversion function
* fix: golint
* refactor: simplify admin user creation
* refactor: move migration script to a separate package