Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Add `GET /admin/stats` (TC-1482), a read-only list of the lowercase wallet addresses that hold data on the node, for the scoreboard's protocol-user count. It is disabled (`404`, like an unknown route) unless the new `NODE_STATS_READ_TOKEN` is set to a value of at least 32 bytes (`openssl rand -hex 32`); a shorter value, or one equal to `TINYCLOUD_ADMIN_SECRET`, also disables it. The token is separate from `TINYCLOUD_ADMIN_SECRET` (neither opens the other's routes). An owner counts when they hold live KV in any space except `account`, or database artifacts in any space except `account` and `secrets`, except KV in the `public` space whose key starts with `.well-known/vault-` (vault metadata). Automatic sign-in records are excluded: the `account` space, the `secrets` table OpenKey's bootstrap creates, and the vault metadata `vault.unlock()` writes. App first-use databases (for example listen's `conversations`) count. So an owner who only signed in, or only signed in and unlocked the vault, is not listed. One SQL statement however many spaces exist. A result past 200,000 owners (or 400,000 distinct counted spaces) is a bare `507`, never a partial `200`. Neither the token nor any address is logged. The deployment workflow and compose files do not pass the variable yet.
- New migration `m20261020_000000_current_kv_key_c_index` adds `idx_current_kv_space_deleted_key_c ON current_kv (space, deleted, (key COLLATE "C"))` on PostgreSQL (TC-1274). The KV list range select compares `key COLLATE "C"`, which the existing index, built in the column's own collation, cannot seek on under a non-`C` database collation such as production's `en_US.UTF-8`; it read and filtered every live key of the space. No query changed: the planner matches the new index. **The index is performance-only: the node boots and serves without it, so the migration never locks `current_kv` for a build and never fails the boot over the index.** It does nothing when a valid index of that exact shape exists; builds it when `current_kv` is empty (a fresh database); and, when it is missing on a non-empty `current_kv`, builds nothing, logs one `current_kv_c_index_missing` warning with constant text and keeps serving (KV lists stay slower), and is recorded as applied, so an index built later is used without a restart. An index of that name that is invalid, not ready or not live logs `current_kv_c_index_invalid`; one of another shape logs `current_kv_c_index_wrong_shape` at error level (drop it with `DROP INDEX CONCURRENTLY`); neither stops the boot. **Operator step before deploy** (PostgreSQL), to avoid the slow lists: build the index with `CREATE INDEX CONCURRENTLY IF NOT EXISTS` outside any transaction and check `pg_index.indisvalid`, `indisready` and `indislive`; the exact statements and recovery are in `docs/releasing.md`. SQLite and MySQL: no change. Rollback: `DROP INDEX CONCURRENTLY IF EXISTS idx_current_kv_space_deleted_key_c`, and, for a binary rollback, `DELETE FROM seaql_migrations WHERE version = 'm20261020_000000_current_kv_key_c_index'` first (an older node refuses to start with a migration it does not ship).
- The SQL/DuckDB legacy-artifact fence scan is reused for 60 s instead of 1 s while the durable fence is off (TC-1274). It was an anti-join over every artifact row, re-run by the first SQL/DuckDB request each second. The durable and configured fences are still read on every request, so `fence on` is immediate; a stale answer can only delay a 503 (or lift one) by at most 60 s, and only around an artifact written by an older release or a restore. The argument is on `LEGACY_SCAN_TTL` in `database_migration.rs`.
- A read invocation of several `kv/get` or `kv/metadata` capabilities (the SDK's `batchGet` and `batchHead`) looks its keys up with one `current_kv` statement per space instead of one per capability (TC-1274). PostgreSQL uses `key = ANY($2)`; SQLite uses `IN` lists of at most 500 keys; MySQL and a space with a single capability keep the per-capability lookup (MySQL's usual collations compare keys case-insensitively, so a row cannot be matched back to the key that asked for it). Capabilities are still answered one at a time in their own order, so response bodies, their order, duplicate and missing or deleted keys, the per-key `x-tinycloud-max-response-bytes` check and which capability's error is reported first are unchanged, and a failed lookup is reported by the first capability of its space. The read-audit record, written before the data is returned, and its `invoked_abilities` rows are unchanged. A 100-key `batchGet` on PostgreSQL goes from 216 to 22 round trips and from 98 to 1 `current_kv` statements. `tinycloud-core` enables sea-orm's `postgres-array` feature for the array parameter.

## [1.21.2] - 2026-10-19

Expand Down
2 changes: 1 addition & 1 deletion tinycloud-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async-std = [
[dependencies]
dashmap = "5.5"
async-trait.workspace = true
sea-orm = { version = "1.1", default-features = false, features = ["macros", "with-time", "with-json"] }
sea-orm = { version = "1.1", default-features = false, features = ["macros", "with-time", "with-json", "postgres-array"] }
sea-orm-migration = { version = "1.1", default-features = false }
futures.workspace = true
pin-project.workspace = true
Expand Down
Loading
Loading