Features
Storage
Connect storage-service and the shell owns the company bucket. Iframe apps upload, list, and pick files through the SDK. New files stay private until you share them.
A company bucket in the shell
The storage-service is a Django API with a Supabase-compatible surface at /storage/v1/*. It verifies identity-service JWTs, stores blobs in S3 or on disk, and enforces company and optional per-user quotas.
Embedded apps do not call that API themselves. They use shellui.storage. The SDK posts to the root shell, which runs the request with storage.url and the signed-in user’s token.
1. Set storage.url
Add the storage-service origin. Optionally set filesUrl for the Files explorer and picker.
2. Read quota in Settings
Signed-in users open Settings → Storage. Hide that page with showInSettings: false if you only want Admin and the SDK.
3. Call the SDK
After init, upload, list, move, and download through shellui.storage.from("company").
import type { ShellUIConfig } from "@shellui/core";
const config: ShellUIConfig = {
storage: {
url: "http://localhost:8001",
filesUrl: "http://localhost:5175/",
},
};
export default config;Files from the iframe
Methods return { data, error } and do not throw. Nested folders are path segments. The picker opens the Files app in a shell modal so the user can choose files or folders without you building a browser.
import { shellui } from "@shellui/sdk";
await shellui.init();
const bucket = shellui.storage.from("company");
const file = new File(["quarter one"], "q1.pdf", { type: "application/pdf" });
const { data, error } = await bucket.upload(
"docs/reports/2024/q1.pdf",
file,
{ upsert: true },
);import { shellui } from "@shellui/sdk";
await shellui.init();
const { data: entries, error } = await shellui.storage
.from("company")
.list("docs/reports", {
limit: 200,
sortBy: { column: "name", order: "asc" },
});import { shellui } from "@shellui/sdk";
await shellui.init();
const { data: blob, error } = await shellui.storage
.from("company")
.download("docs/reports/2024/q1.pdf");
if (!error) {
const url = URL.createObjectURL(blob);
}import { shellui } from "@shellui/sdk";
await shellui.init();
const result = await shellui.selectFiles({ multiple: true });
if (!result) return;
for (const file of result.items) {
console.log(file.id, file.path);
}Private by default
There is one system bucket per company, named company. New folders and files belong to the creator until you add an access grant. Nested items inherit the parent folder. There is no anonymous public bucket — use a share link for people outside the company.
Access grants
Allow or deny read, write, or admin for a user, group, or the whole company, on a folder or a file.
Share links
Secret URLs for one file, with an expiry and/or a download cap. No account required. Revoke them when the review is over.
Quotas
A company total, plus an optional per-user cap. Overflow returns 413. Settings shows usage to the signed-in user.
await fetch(`${storageUrl}/storage/v1/access/grant`, {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
bucket: "company",
subject_type: "user",
subject_id: "42",
resource_type: "folder",
resource_id: "hr",
permission: "write",
effect: "allow",
}),
});const quota = await fetch(`${storageUrl}/storage/v1/quota`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}).then((response) => response.json());Next, see how the Architecture pieces — shell, identity, admin, storage, and files — fit together.