Migrating to v5
v5.0.0 breaks the public surface on purpose. The old spellings are gone rather than deprecated: carrying both for a release line would leave Atlas with two vocabularies, which is the thing this release exists to end.
Nothing about stored data changes. Snapshots, manifests, blobs and delta cursors written by v4 are read by v5 unchanged, and a v5 restore of a v4 snapshot needs no migration step. What changes is the SDK's field names, the CLI's flags, and the exit codes.
SDK: one convention
Every option and result field on the public surface is camelCase. Methods and config already were.
// v4
const result = await atlas.outlook.backup(mailboxId, { force_full: true, page_size: 25 });
console.log(result.summary.attachments_stored, result.graph_cost?.requests_total);
// v5
const result = await atlas.outlook.backup(mailboxId, { forceFull: true, pageSize: 25 });
console.log(result.summary.attachmentsStored, result.graphCost?.requestsTotal);The compiler is your migration tool. The option and result types are exported, so a TypeScript build fails at every renamed field and the completion list offers the new name. There is deliberately no codemod: a transform would only help JavaScript consumers, who are also the ones it is riskiest for.
The rule is mechanical, so the table below is a spot check rather than a dictionary: snake_case becomes camelCase, one field at a time, at every depth.
| v4 | v5 |
|---|---|
force_full | forceFull |
page_size | pageSize |
folder_filter | folderFilter |
file_filter | fileFilter |
folder_name | folderName |
message_ref | messageRef |
snapshot_id | snapshotId |
target_mailbox | targetMailbox |
output_path | outputPath |
skip_integrity_check | skipIntegrityCheck |
include_recoverable_items | includeRecoverableItems |
include_subsites | includeSubsites |
object_lock_request | objectLockRequest |
retention_days | retentionDays |
retain_until | retainUntil |
start_date / end_date | startDate / endDate |
restored_count | restoredCount |
saved_count | savedCount |
attachments_stored | attachmentsStored |
integrity_failures | integrityFailures |
verification_warnings | verificationWarnings |
total_checked | totalChecked |
manifests_in_chain | manifestsInChain |
graph_cost | graphCost |
requests_total | requestsTotal |
elapsed_ms | elapsedMs |
last_backup_at | lastBackupAt |
total_size_bytes | totalSizeBytes |
Three things that deliberately did not change
Map keys Atlas did not choose. deltaLinks is keyed by Graph folder id, requestsByType by request label, byService by pool name. The field names became camelCase; the keys inside are data and are untouched. byService.sharepoint_onedrive is still spelled that way, because the same identifier appears in GRAPH_SERVICE_LIMITS and renaming it in one place only would be worse than leaving it.
The raw Graph payload. readMessage() returns message exactly as Graph produced it, including @odata.etag and internetMessageHeaders. Renaming inside it would hand back a message that no longer matches the stored blob.
StorageTarget. The value createStorageTarget() returns is a handle you pass straight back, not data you author, so it is unchanged. Its config is camelCase, and now camelCase only:
// v4 accepted either spelling
createStorageTarget({ s3_endpoint: '...', encryption_passphrase: '...' });
// v5 accepts the documented one
createStorageTarget({ s3Endpoint: '...', encryptionPassphrase: '...' });Exports are now enumerated
v4's entry point re-exported all of @wisecom/atlas-types, which published every internal port, DI token and service interface as public API. v5 exports a named list: the instance and its three workload APIs, their option and result types, the progress and error types, and the two value exports. If you imported an internal port, a token, or a use-case interface, it is no longer public. Those were never intended as API and had no stability guarantee; open an issue if you were relying on one and it will be considered on its merits.
create_container and create_container_from_config are no longer exported, as completed in PR #323. The former discovered .env and atlas.config.json, contradicting the SDK's explicit-configuration contract; the latter was an internal DI factory. Pass every credential to createAtlasInstance instead. The unused environment-loading factory has also been removed from the SDK source.
Eager configuration validation
createAtlasInstance now throws ConfigError synchronously for missing or blank required fields, passphrases shorter than 14 UTF-8 bytes, and malformed S3 endpoints. Endpoints must be absolute HTTP(S) URLs without embedded credentials, a query or a fragment. Previously some invalid values survived construction and failed only during an operation.
Existing short-passphrase backups need care. Do not pad or replace the passphrase to satisfy the new check: that changes the key and makes existing data unreadable. Keep the original passphrase and use the previous SDK release or the CLI to recover those backups. This change does not rotate keys or rewrite stored data; the CLI's existing passphrase handling is unchanged.
The optional await atlas.validate() checks an existing tenant bucket, unwraps _meta/dek.enc with the configured passphrase when that object exists, then acquires a Graph token. It creates no storage. A fresh tenant with no wrapped key passes; a wrong passphrase for existing backups raises WrongPassphraseError before Graph is probed. This passphrase check was added in v5.2.0; v5.0.0 and v5.1.0 validated only HeadBucket and Graph token acquisition. The method still does not verify workload permissions. S3-stage failures are StorageError; Graph-token failures are AuthError. See Configuration validation for prerequisites and error codes.
Together with the camelCase surface and enumerated exports in PR #323, this completes issue #45. The error classes were already available from v4.4.0; this change uses them at construction and in the explicit connectivity probe.
CLI failure exit codes
v4 returned 1 for every fatal exception. v5 uses the typed error categories introduced in v4.4.0, with the original Graph, AWS or Node failure reported separately on stderr.
| Failure | v4 | v5 |
|---|---|---|
| Throttle exhaustion or recognized network/transient HTTP failure | 1 | 3 |
| Authentication, permission or mailbox-license failure | 1 | 4 |
| Wrong encryption passphrase | 1 | 5 |
| Typed configuration failure or failure to load CLI configuration | 1 | 6 |
| Missing resource | 1 | 7 |
| Typed Object Lock retention refusal | 1 | 8 |
| Unexpected or unclassified failure, usage error, command-reported failure without a typed exception | 1 | 1 |
| Complete operation | 0 | 0 |
| Partial operation or soft interrupt | 2 | 2 |
Replace checks that only recognize 1 as failure. A script using if [ "$status" -eq 1 ] would miss the new failure categories; test for nonzero first, then branch on the documented category when deciding whether to retry. Codes 4 through 8 require operator action rather than an unchanged retry. Code 3 permits scheduling a later attempt, but a timed-out write may already have committed.
Atlas code: now identifies the Atlas category; Transport code:, HTTP status:, Error name: and Body: describe its underlying cause. Fatal details that previously went to stdout now go to stderr. Update log collectors accordingly, and use exit codes rather than parsing message wording. The CLI reference lists the full mapping, precedence and command-reported exceptions.
CLI: one vocabulary for flags and verbs
-s is the snapshot on every command that has one, and -o is the owner on every command that has one. Two commands spelled those letters differently, so the flags they collided with give up their short form rather than keep a second meaning: --site and --output now have no short spelling anywhere, and nothing can shadow -s or -o again.
Short flags whose meaning changed
A retired short flag is rejected rather than reinterpreted. Silently resolving atlas stats -s <site> as a snapshot id would report on the wrong scope, and atlas outlook save -o <path> as an owner would put the archive somewhere else. That is a data bug wearing a breaking change's clothes, so the old spelling fails and names its replacement.
| v4 | v5 | Using the old form |
|---|---|---|
atlas stats -s <site> | atlas stats --site <site> | error naming -s, exit 1 |
atlas outlook save -o <path> | atlas outlook save --output <path> | error naming -o, exit 1 |
atlas onedrive save -O <path> | atlas onedrive save --output <path> | error naming -O, exit 1 |
atlas sharepoint save -O <path> | atlas sharepoint save --output <path> | error naming -O, exit 1 |
$ atlas stats -s https://contoso.sharepoint.com/sites/finance
error: option '-s <value>' argument 'https://contoso.sharepoint.com/sites/finance' is invalid. -s no longer means --site in v5.0.0. Pass --site instead.The retired spellings are hidden from --help, because they are not flags you can use. They exist only so a script that still passes one stops instead of doing the wrong thing.
-f takes one folder and repeats
atlas outlook backup took a variadic -f <name...> while restore and save took a single value, so one command group carried two arities and a variadic -f swallowed the next flag's argument. Every Outlook command now takes one folder per flag, repeated:
| v4 | v5 |
|---|---|
atlas outlook backup -f Inbox "Sent Items" | atlas outlook backup -f Inbox -f "Sent Items" |
A single-folder invocation is unchanged. The old multi-folder form fails with error: too many arguments for 'backup'. Expected 0 arguments but got 1: Sent Items. and exit 1; the first folder is still read from -f, so the command does not quietly back up less than you asked for.
Verbs that replaced flags and positionals
A flag that changes what a command does is a verb wearing the wrong clothes, and a positional that accepts either a key or the word list cannot document itself in --help.
| v4 | v5 | Using the old form |
|---|---|---|
atlas replicate --status | atlas replicate status | error: unknown option '--status' |
atlas config <key> <value> | atlas config set <key> <value> | error: unknown command '<key>' |
atlas config <key> | atlas config get <key> | error: unknown command '<key>' |
atlas config list | unset | validate | unchanged | still works |
list, unset and validate were already spelled as words, and are now real subcommands rather than magic values in a positional, so atlas config list --help documents itself. The replication scope flags are declared on both replicate and replicate status, so each --help is complete.
Validation moved to the flag boundary
--lock-mode and --service are validated by the argument parser instead of by hand, so a typo fails before Atlas opens a Graph or S3 connection rather than after:
$ atlas sharepoint backup --site <site> --lock-mode bogus
error: option '--lock-mode <mode>' argument 'bogus' is invalid. Allowed choices are governance, compliance.A script that matched the old wording needs the new strings. The v4 messages were Invalid object lock mode "bogus". Expected "governance" or "compliance". from the policy layer and Unknown --service "x"; expected outlook, onedrive, sharepoint, or all from the stats command.
Defaults are declared to the parser too, so --help shows --top as (default: "20") and --conflict as (default: "rename") instead of repeating the value in prose. The values themselves are unchanged.
SDK: exports can stream
Additive, not breaking. save() and saveMailbox() now accept output, a Node Writable, so an export can go straight to an HTTP response or an upload instead of a local file. Existing outputPath callers are unaffected.
// v5, no local file involved
await atlas.outlook.save(snapshotId, { output: res });Passing both output and outputPath throws ConfigError. For a stream export the result reports outputPath: '', and a failed run destroys the stream rather than ending it. See Exporting to a Stream, which closes issue #44.
Next
Two v5.0.0 items are still in flight and are documented here as they merge: --fast for drive verification, and the --json rollout across the remaining commands (issue #94).