Skip to content

Fix the CLI, backup and restore follow-ups of M4 and M6 - #3122

Merged
pournasserian merged 4 commits into
mainfrom
3068-cli-backup-followups
Nov 15, 2026
Merged

pournasserian merged 4 commits into
mainfrom
3068-cli-backup-followups

Conversation

@pournasserian

@pournasserian pournasserian commented Nov 15, 2026 •

Copy link
Copy Markdown
Member

Closes #3068

The low items of the review of M4 and M6 for fluentcms, the mcp proxy, backup and restore, and the items of the two comments. Each one is fixed with a test, except the GET stream of mcp and a second way to find a host on the rollback journal (see Declined). The second commit fixes the findings of the PR review.

Decisions

  • Item 2, schema check. restore compares the schema hash and the applied migrations of every stream in backup.json with the host before it writes. A different hash, a stream the host does not have, or a migration the host's set lacks is refused with FCMS3249. The schema check has its own option, --allow-schema-mismatch, so --force keeps meaning only "replace the existing database and media files" and the usual restore over a database is still checked. With the option the restore goes on, and the difference is printed as a warning once the database is in place, so a later refusal (FCMS3245, FCMS3246, FCMS3242) never follows a "restored" line. A migration the host has and the backup had not is pending, not a difference.
  • Item 3. The FCMS3244 text and the guide say to stop every host first, because one on the rollback journal is not seen. No second detection was added (see Declined).
  • Item 5.
    • IDatabaseFile.Check runs PRAGMA quick_check on the unpacked file, and refuses an empty one, before the database is touched. The refusal is FCMS3242.
    • Restore moves the database and its -wal, -shm and -journal aside together. If one of them or the snapshot cannot be moved, it moves them back. It deletes them only after the snapshot is in place.
    • If moving back fails too, the error (FCMS3307) names the <db>.<guid>.replaced* files.
  • Item 6. The archive and the snapshot beside it are created with Unix mode 600. On Windows they take the folder's permissions, and docs/backups.md says how to keep the archive.
  • Item 7.
    • A usage error is written from the arguments with the value after --token (also --token= and --token:) replaced by ***.
    • The tool reads no @file response file. System.CommandLine expanded one before the key could be masked; a test showed it.
    • docs/docker.md shows an MCP client configuration with the key in env.
  • Items 8 and 9. InstanceOptions.Resolve now warns with FCMS3363 (once, for every --url command) and refuses a key with a character outside ! to ~ with FCMS3316 before any request. --site of export and import gets the same check as bad usage.
  • Item 10.
    • A command given --url gets a termination timeout of 10 seconds: Ctrl+C or SIGTERM cancels it and it cleans up. It exits 1, or 130 (Ctrl+C) or 143 (SIGTERM) if it is not done by then.
    • The exception is schema diff --url without --schema, which runs the host for its schema and so behaves like a project command, as all project commands do: no timeout, and the host is not killed.
  • Item 4, mcp.
    • Standard input and event streams are read by BoundedLineReader, which decodes UTF-8 itself and takes what each read gives. StreamReader.ReadAsync kept reading while each read filled its 1 KiB buffer, so a line of exactly 1024 or 2048 bytes stalled on a pipe.
    • A line ends at LF, CR or CRLF, as before.
    • initialize is sent without the session and the protocol version, and a 404 to a request that named the session clears both.
    • A request gets a JSON-RPC error when its event stream or JSON answer leaves it out, its stream breaks before its answer, or the server answers 202/204.
    • A request already answered never gets a second answer. Ids are compared by value, exactly (significant digits and exponent), so 1 answers 1.0 and 1e-30 is not 0.
    • A batch that fails gets a batch of errors.
    • A line of input, an answer and an event are each bounded to 16 MiB of UTF-8 bytes (FCMS3317).
    • After standard input ends, the calls in flight have 30 seconds before they are cancelled.
  • Item 12 and the comments.
    • One helper, PrintableText in Core, writes control characters and bidi controls as \uXXXX.
    • It is applied to:
      • the parts of a problem;
      • the output of the remote commands, a job's lastError included;
      • the import report;
      • the text of doctor, where only the host's own refusals keep their line breaks;
      • schema diff;
      • the generate warnings;
      • the field types schema validate notes;
      • the messages of SchemaBuilder and SchemaValidator.
    • As a safety net it is applied to every message the tool and the proxy write to standard error, keeping their own line breaks.
    • JSON output and snapshot bytes are unchanged. The TypeScript Literal escapes the same characters, and FCMS3370 now says ASCII.
  • Item 1. user add-role --url reads the pages of GET /users until it finds the address or reaches the total. No API change.
  • Item 11. The host parser treats --help, -h and -? as help only where an option or an argument stands. As the value of an option (--name=-h, handed on as --name -h) they are that value.
  • Codes. Both new codes are registered in ErrorCodeRegistry, and the spec's range sentences say so. No log event id was added.
    • FCMS3249: the restore schema mismatch, the next free code of the backup and restore block (FCMS3240 to FCMS3259), in TransferCommandErrorCodes.
    • FCMS3317: the MCP size cap, in the tool's range (FCMS3310 to FCMS3319).

Declined

  • Item 4, GET stream of mcp. The plugin serves statelessly and sends everything on the answer to each POST, as 6.1 says. Opening a stream for servers with sessions is a feature, not a follow-up. A request that such a server accepts without an answer now gets a JSON-RPC error instead of hanging.
  • Item 4, a dropped line of input over 16 MiB. Its id cannot be read without reading the line, so it gets no JSON-RPC error, only a note on standard error. The instance refuses requests over 1 MiB by default anyway.
  • Item 3, another way to find a running host. An idle connection on the rollback journal holds no lock, and the other options (a lock file, /proc) do not work across containers or need a new runtime mechanism. The refusal texts and the guide say so instead.

Tests

  • tests/FluentCMS.Cli.Tests:
    • InstanceCommandTests: paging of user add-role; FCMS3363 for every --url command; a key or a site with a line break; escaped problem, job error, doctor report (line breaks included) and diff; an export cancelled mid-read leaves no file.
    • McpProxyTests:
      • expired session, a stream that ends without the answer, 202, a refused batch, an oversized answer;
      • the drain timeout, and a cancelled proxy ends its session;
      • an answer written before its stream broke gets no error;
      • a JSON answer that leaves out a request;
      • ids compared by value, exactly;
      • lines on an anonymous pipe with the writer held open (1023 to 65536 bytes, and 3-byte characters), and a 1 KiB event on a pipe; all fail with the earlier StreamReader path;
      • lone CR;
      • the bound of a line counted in bytes with 3-byte characters.
    • ParserTests: no key in a usage error; no response file; the termination timeout only for --url without a host; the unchecked field type note; --allow-schema-mismatch handed on.
    • GenerateTextTests: the TypeScript literal, select options, an escaped FCMS3370 and its ASCII wording.
  • tests/FluentCMS.Core.Tests:
    • PrintableTextTests: the helper, schema reader and validator messages, the import report.
    • CommandParserTests: --name=-h and --name -h.
  • tests/FluentCMS.Data.Tests:
    • BackupCommandTests:
      • FCMS3249 refused even with --force, and allowed with --allow-schema-mismatch, with the warning only after the swap;
      • a migration the host has and the backup lacks is no difference;
      • a database entry that is not SQLite, or is empty, keeps the existing database;
      • mode 600 on Unix;
      • the FCMS3244 text.
    • DatabaseFileTests: a failed swap puts the database and its journals back; Check on whole, cut, text and empty files.

Full CI run: 43661544445 on cde51d1 (success); the first round was 43656743485 on fe261a8 (success).

@pournasserian pournasserian added type:bug Defect area:operations Health, observability, limits, locks and diagnostics labels Nov 15, 2026
@pournasserian
pournasserian enabled auto-merge (squash) November 15, 2026 14:38
@pournasserian
pournasserian merged commit 7d3fd39 into main Nov 15, 2026
14 checks passed
@pournasserian
pournasserian deleted the 3068-cli-backup-followups branch November 15, 2026 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:operations Health, observability, limits, locks and diagnostics type:bug Defect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI, backup and restore follow-ups from the review of M4 and M6

1 participant