@agglabs-one/object
v0.1.5
Published
Node SDK for AGG One Object Storage — upload, list, link and delete files.
Maintainers
Readme
@agglabs-one/object
Node SDK for AGG One Object Storage. Upload, list, link and delete files over the API with a scoped key. Zero runtime dependencies; Node 18+.
Install
npm install @agglabs-one/objectQuick start
import { randomUUID } from 'node:crypto';
import { ObjectStorage } from '@agglabs-one/object';
const store = new ObjectStorage({ key: process.env.AGG_STORAGE_KEY! });
// Upload a local file — name and content-type are inferred from the path.
const obj = await store.upload('./contract.pdf', {
path: `/contracts/${randomUUID()}`,
visibility: 'private',
});
// Download it back (a fresh signed link is fetched automatically).
await store.download(obj.id, './downloaded.pdf');Create a key in your account under Object Storage → Preferences → API Access. A key is scoped to the project it's created in, and shown only once.
API
new ObjectStorage(options)
| Option | Default | |
| --- | --- | --- |
| key | — | Required. agk_<prefix>_<secret>. |
| baseUrl | https://object.one.agglabs.com | Override the API host. |
| timeoutMs | 120000 | Per-request timeout. |
| encryptionKey | — | Base64 of a 32-byte key for locking uploads. See Encryption. |
upload(input, options?) → Promise<StorageObject>
input is a file path, a Buffer/Uint8Array, or a Readable stream —
streamed to the server, never buffered whole. Needs storage:write.
await store.upload('./logo.png', { path: '/assets', visibility: 'public' });
await store.upload(buffer, { name: 'report.json' });
await store.upload(fs.createReadStream('./big.zip'), { name: 'big.zip' });Options: name (required unless uploading a path), path (default /),
visibility (public | private, default = bucket setting), contentType
(guessed from name), overwrite (replace a same-named file), lock (encrypt
this object with your encryptionKey — see Encryption).
list(options?) → Promise<Listing>
Lists one folder. { path } defaults to /. Needs storage:read.
const { folders, objects, usage, maxBytes } = await store.list({ path: '/contracts' });link(id) → Promise<StorageObject>
A fresh object record — a private object's url is newly signed. Needs storage:read.
download(idOrObject, destPath) → Promise<StorageObject>
Streams an object to a local file, fetching a fresh link first so an expired signature never bites.
remove(id) → Promise<void>
Deletes an object. Needs storage:delete.
Encryption (locked objects)
Lock a file with a customer key the server never stores. A locked object is
encrypted at rest (AES-256-GCM), can't be opened via any public or signed link,
and decrypts only through download() with the same key.
// Generate a key ONCE and store it somewhere durable (a secret manager).
const encryptionKey = ObjectStorage.generateKey(); // base64 of 32 random bytes
const store = new ObjectStorage({ key: process.env.AGG_STORAGE_KEY!, encryptionKey });
// Lock on upload — the object is forced private and cannot be served publicly.
const obj = await store.upload('./contract.pdf', { path: '/contracts', lock: true });
console.log(obj.locked); // true — `obj.url` is empty for a locked object
// download() and remove() send the key automatically when it's configured.
await store.download(obj, './contract.pdf');
await store.remove(obj.id);lock: truerequiresencryptionKeyin the client options, oruploadthrows.- Reading or deleting a locked object without the right key returns 403.
storage:readalone is not enough for a locked object — the key authorizes reading the bytes, the API key only authorizes the call.
⚠️ Lost key = unrecoverable file. The server never stores your key and cannot reset or recover it. If you lose it, every file locked with it is gone for good. Rotating a key means re-uploading the file with the new key.
Errors
Every non-2xx response throws a typed error you can branch on:
import { InsufficientScopeError, ConflictError } from '@agglabs-one/object';
try {
await store.upload('./x.pdf', { name: 'x.pdf' });
} catch (err) {
if (err instanceof ConflictError) { /* already exists — pass { overwrite: true } */ }
if (err instanceof InsufficientScopeError) { /* key lacks storage:write */ }
}StorageError (base) · InvalidKeyError (401) · InsufficientScopeError (403)
· NotFoundError (404) · ConflictError (409). Each carries .status and .code.
Types
StorageObject, Listing, Visibility, ClientOptions, UploadOptions,
ListOptions, and UploadInput are all exported.
