Core
Framework-agnostic upload, URL, and callback primitives.
@silo-storage/sdk-core is the lowest-level Silo SDK package. It exposes the pieces needed to talk to Silo directly without taking a dependency on a server framework or UI layer.
Use it when you want to build your own server integration, generate file URLs directly, or verify signed callbacks without the route abstraction from @silo-storage/sdk-server.
What it provides
createSiloCoreFromTokencreateSiloClient- single-file and batch upload registration helpers
- signed and public URL generation
- file listing and file detail APIs
- file access, expiry, and delete APIs
- callback signature verification
- dev SSE consumption for local workflows
Install
npm install @silo-storage/sdk-coreCreate a core client
import { createSiloCoreFromToken } from "@silo-storage/sdk-core";
const core = createSiloCoreFromToken({
url: process.env.SILO_URL!,
token: process.env.SILO_TOKEN!,
cdnHost: process.env.SILO_CDN ?? process.env.NEXT_PUBLIC_SILO_CDN!,
callbackUrl: "https://app.example.com/api/upload",
});This gives you a framework-agnostic upload client with enough configuration to:
- register uploads
- generate signed or public file URLs
- verify signed callbacks
- make authenticated file management requests
If you want to share defaults such as apiBaseUrl, cdnHost, and a default token across multiple core instances, use createSiloClient(...):
import { createSiloClient } from "@silo-storage/sdk-core";
const silo = createSiloClient({
apiBaseUrl: process.env.SILO_URL!,
cdnHost: process.env.SILO_CDN!,
token: process.env.SILO_TOKEN!,
});
const core = silo.createSiloCoreFromToken();Upload strategies
The core client supports two upload strategies:
server: calls Silo's combined upload endpoint and returns a ready-to-use signed upload URLself: registers the upload first and signs the upload URL locally
server is the default and is what higher-level packages use unless you override it.
Use server unless you specifically need local signing behavior.
Core upload flow
At the sdk-core level, uploads usually look like this:
- your server prepares or registers an upload with Silo
- your client uploads bytes to the returned
uploadUrl - Silo sends a signed callback to your server if you configured
callbackUrl
Higher-level packages such as @silo-storage/sdk-server and @silo-storage/sdk-next build on top of this flow.
Prepare an upload
Use prepareUpload(...) when you are dealing with a single file and want the simplest API.
const prepared = await core.prepareUpload({
file: {
fileName: "photo.png",
size: 1234,
mimeType: "image/png",
},
});The returned prepared.file includes the values your client needs to actually upload the file:
uploadUrluploadMethodaccessKeyfileKeyIdexpiresAt
If you need to force a strategy per call:
await core.prepareUpload({
uploadStrategy: "server",
file: {
fileName: "photo.png",
size: 1234,
},
});Direct fetch() uploads
Upload URLs default to the resumable tus ingest path. If you want a plain signed URL for fetch(...), pass uploadMethod: "put":
const prepared = await core.prepareUpload({
uploadMethod: "put",
file: {
fileName: file.name,
size: file.size,
mimeType: file.type || undefined,
},
});
await fetch(prepared.file.uploadUrl, {
method: "PUT",
headers: file.type ? { "Content-Type": file.type } : undefined,
body: file,
});Register a batch of files
Use registerUploadBatch(...) when you want to register more than one file in one call, or when you want more direct control over callback metadata and registration behavior.
const registered = await core.registerUploadBatch({
callbackUrl: "https://app.example.com/api/upload/callback",
callbackMetadata: {
userId: "user_123",
source: "dashboard",
},
files: [
{
fileName: "photo.png",
size: 1234,
mimeType: "image/png",
acceptedMimeTypes: ["image/png", "image/jpeg"],
},
{
fileName: "manual.pdf",
size: 45_000,
mimeType: "application/pdf",
acceptedMimeTypes: ["application/pdf"],
},
],
});If you provide callbackUrl in production, it must be an absolute public URL. You can pass it once when creating the core client, override it per request, or omit it entirely if you do not need upload callbacks.
The result shape depends on the mode:
- production returns prepared files plus registration info
- development returns prepared files plus SSE streams for local workflows
If you only need one file, prefer prepareUpload(...).
Generate file URLs
sdk-core also gives you direct URL helpers when you already know a file's access key or metadata.
const signedDownloadUrl = await core.generateDownloadUrl("file-access-key"); // this will generate a signed URL
const publicDownloadUrl = await core.generateDownloadUrl("file-access-key", {
sign: false, // don't sign the URL (since we know it's public)
});
const imageUrl = await core.generateImageUrl("file-access-key", {
width: 800,
format: "webp",
quality: 80,
}); // this will generate a signed image URL for a webp of width 800px and quality level 80You can also generate URLs from an explicit file-like object:
const downloadUrl = await core.generateDownloadUrl({
accessKey: "file-access-key",
isPublic: false,
fileName: "photo.png",
});For more detail on URL generation, callback URLs, and low-level callback verification, read URLs and Callbacks.
Verify callbacks directly
If you are not using sdk-server, you can verify and parse signed callbacks yourself:
import {
parseSiloToken,
verifyAndParseUploadCallback,
} from "@silo-storage/sdk-core";
const signingSecret = parseSiloToken(process.env.SILO_TOKEN!).signingSecret;
export async function POST(request: Request) {
const callback = await verifyAndParseUploadCallback({
request,
signingSecret,
});
return Response.json({
metadata: callback.metadata,
data: callback.data,
});
}Use this when you want raw access to the callback envelope. If you want route-aware callback dispatch and typed onUploadComplete(...) handlers, use Server instead.
Manage existing files
Beyond upload preparation, sdk-core also exposes basic file management APIs.
List files
You should not rely on this endpoint for your UI and business logic. Always store the file access key in your database and use it to generate URLs.
const result = await core.listFiles({
page: 1,
pageSize: 20,
status: "completed",
});Get file detail
const file = await core.getFile({
projectId: "proj_123",
fileKeyId: "filekey_123",
});Update file access
await core.updateFileAccess({
projectId: "proj_123",
fileKeyId: "filekey_123",
isPublic: true,
serveImage: true,
});Update file expiry
await core.updateFileExpiry({
projectId: "proj_123",
fileKeyId: "filekey_123",
ttlSeconds: 60 * 60 * 24 * 30, // 30 days from now
});await core.updateFileExpiry({
projectId: "proj_123",
fileKeyId: "filekey_123",
expiresAt: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000), // 30 days from now
});Delete a file
const deleted = await core.deleteFile({
projectId: "proj_123",
fileKeyId: "filekey_123",
});deleteFile(...) accepts either fileKeyId or accessKey. It returns the deletion message plus any lifecycle cleanup work claimed by the API.
These APIs are useful for admin panels, background jobs, and internal tooling where you need to inspect or modify uploaded files after registration.
Dev mode and SSE
When registration runs with dev: true, Silo can return an SSE stream instead of the normal production registration response.
Use consumeDevRegisterSse(...) when you want to process:
connectedchunkkeepaliveerror
This is intended to be used for local development with the SDK when the local dev server is not accessible from the internet.
API reference
For generated type tables covering client creation, upload registration, and callback verification, see API Reference.
When to use sdk-core directly
Reach for sdk-core when:
- you want to integrate Silo into a custom backend runtime
- you want to stay below the route abstraction from
sdk-server - you need direct URL generation APIs
- you need file listing, update, or delete APIs
- you want callback verification without the upload router abstraction
If you want route-based uploads with typed middleware and completion handlers, move up to Server.
If you are using Next.js App Router and want the full request handler integration, move up to Next.