@ogi-sdk/nimbus
v0.1.0
Published
Typed client for the nimbus cloud save server
Readme
@ogi-sdk/nimbus
Typed client for a nimbus cloud save server. Route types are generated from the server's Elysia app, so a protocol change fails to compile here rather than at runtime in an addon.
bun add @ogi-sdk/nimbus effectSessions
import { Effect } from 'effect'
import { NimbusClient, SaveCipher } from '@ogi-sdk/nimbus'
const nimbus = new NimbusClient({ baseUrl: 'https://nimbus.example.com' })
// login and signup solve the proof-of-work challenge for you
const session = yield* nimbus.login({ username, password, deviceName: 'Desktop' })
const client = nimbus.withSession(session)
const cipher = new SaveCipher(session.encryptionKey)Persist deviceToken, deviceId, and encryptionKey. The token is revocable from any device; the key never leaves this one.
Syncing
pushSnapshot encrypts every file, sends the manifest, uploads only the blobs the server lacks, and commits. pullSnapshot does the reverse and verifies every plaintext hash.
import { pullSnapshot, pushSnapshot } from '@ogi-sdk/nimbus'
const key = { storefront: 'steam', appId: '413150' }
const layouts = yield* client.bindings(key, { os: 'windows', env: 'goldberg' })
// resolve each layout's root on this machine, glob its files, then:
const version = yield* pushSnapshot(client, cipher, key, files, { baseVersion: lastSyncedVersion })
const { head, files } = yield* pullSnapshot(client, cipher, key, {
skip: (file) => localPlaintextHashes.get(file.path) === file.plaintextHash,
})Paths in files are relative to the layout root and must sit inside one of the app's layouts, or the manifest is rejected.
Errors
Every call fails with a tagged error, so Effect.catchTag works:
| Error | When |
| --- | --- |
| VersionConflictError | Server head moved past baseVersion. Pull, merge, push again or pass force. |
| QuotaExceededError | App or account byte limit hit. Nothing was written. |
| NoSaveDataError | saveInfo / pullSnapshot for an app this account never synced. |
| UnauthorizedError | Missing or revoked device token. |
| IntegrityError | Downloaded bytes did not hash to the manifest. |
| CryptoError | Decrypt failed: wrong key or tampered blob. |
| NetworkError | fetch itself failed. |
| RequestError | Any other non-2xx, with the raw body. |
Lower level
manifest, putBlob, getBlob, commit, saveInfo, listSaves, listDevices, deleteDevice, appDetails, storefronts, and solveChallenge are all exposed on NimbusClient if the snapshot helpers do not fit.
