blocks-data-storage
Skill42 starsChanged 12 days agoMITView on GitHub
Build file and document-management features on SELISE Blocks Data: upload/download, directory trees, cursor-paginated browsing and search, file versions, rename/move/copy, soft delete/trash/restore, sharing, access policies, and inheritance. Use for attachments, file browsers, folders, shared files, permissions, or version history. Use 'blocks data files *' for terminal/admin work and @seliseblocks/client data.files/data.directories/data.objects for app code.
In this repository27 other files it gives its agents.
- AGENTS.mdAGENTS.md
- bootstrap/agents.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- blocks-bootstrapSkill
- blocks-captchaSkill
- blocks-data-gateway-configurationSkill
- blocks-data-gateway-crudSkill
- blocks-frontend-local-httpsSkill
- blocks-functionsSkill
- blocks-iam-access-controlSkill
- blocks-iam-accountSkill
- blocks-iam-mfaSkill
- blocks-iam-organizationsSkill
- blocks-iam-sso-oidc-configurationSkill
- blocks-iam-sso-oidc-implementationSkill
- blocks-iam-usersSkill
- blocks-localization-configurationSkill
- blocks-localization-implementationSkill
- blocks-mailSkill
- blocks-notificationSkill
- blocks-notifierSkill
- blocks-proxySkill
- blocks-release-deploymentSkill
- blocks-secretsSkill
- blocksSkill
- blocks-storage-configurationSkill
- blocks-workflowSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 6 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 6
When invoking a project-scoped blocks command, either use the resolved account's saved selection or pass --project <tenantId> for that one …
- Distinctive: line 10
Treat storage as one permission-aware object tree containing directories and files.
- Very specific: line 16
blocks-storage-configuration: only to manage the named provider configuration used by configurationName.
- Very specific: line 31
Use blocks data files * for terminal/admin work and run blocks help data files <command> --json for exact flags.
- Very specific: line 35
Use blocksClient.data.files for bytes, metadata, versions, and file operations.
- Very specific: line 36
Use blocksClient.data.directories for directory create/get/update/delete/move.
- Very specific: line 37
Use blocksClient.data.objects for browse/search/trash/shared/restore/share/access/inheritance.
- Distinctive: line 54
Prefer the composed command.
- Distinctive: line 63
Use the two explicit commands only when the intermediate upload URL is required:
- Very specific: line 93
Upload completion: When the storage configuration lists the file's access modifier in uploadCompletionRequiredFor (see …
- Very specific: line 104
When parentDirectoryId is empty, the cloud upload resolves moduleName to that module's default directory.
- Distinctive: line 242
Every storage request passes two checks:
- Very specific: line 249
Do not infer permission from a visible button or endpoint grant.
- Distinctive: line 260
Never send Blocks auth headers to a pre-signed provider URL.
- Distinctive: line 261
Never use a raw API call to work around a missing CLI/SDK wrapper.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
When invoking a project-scoped blocks command, either use the resolved account's saved selection or pass --project <tenantId> for that one command without changing saved state. --project applies to CLI commands only, never SDK calls.
Blocks Data — Storage
Treat storage as one permission-aware object tree containing directories and files. Uploading a new file now creates the file object in that tree; there is no second DMS registration step.
Use:
blocks data files *for supported terminal/admin operations.@seliseblocks/clientfor app code. Use the sharedblocksClientcreated by blocks-bootstrap.- blocks-storage-configuration only to manage the named provider configuration used by
configurationName.
Select a project first with blocks use <tenantId>. If login/project state or the shared client setup is unknown, use blocks-bootstrap before this skill.
Store the returned fileId in a Data record when attaching a file to domain data. Use blocks-data-gateway-crud for the record mutation.
Choose the surface
The current CLI and SDK follow the backend's file, directory, and object resource groups.
blocks --version
blocks --help- Use
blocks data files *for terminal/admin work and runblocks help data files <command> --jsonfor exact flags. Do not probe a subcommand with--help; subcommands do not consistently treat it as help and may execute real logic. Thehelpcommand never reaches a handler. - Use
blocksClient.data.filesfor bytes, metadata, versions, and file operations. - Use
blocksClient.data.directoriesfor directory create/get/update/delete/move. - Use
blocksClient.data.objectsfor browse/search/trash/shared/restore/share/access/inheritance.
Legacy data.dms.*, dms-upload, dms-list, create-folder, and delete-folder wrappers are retired. Uploads need no registration call.
Upload a new file
Choose one path from the project's storage configuration.
| Provider category | Sequence |
| Cloud object storage | request a pre-signed URL, then PUT the bytes to it |
| Local/SFTP storage | send one authenticated multipart upload |
Both paths create the file object and initial version directly. Do not call dms-upload afterward.
Cloud upload from the CLI
Prefer the composed command. It previews and confirms both metadata creation and the provider PUT:
blocks data files upload --file ./invoice.pdf --parent-id <directoryId> \
--configuration-name Default --access-modifier Private --dry-run --json
blocks data files upload --file ./invoice.pdf --parent-id <directoryId> \
--configuration-name Default --access-modifier Private --yes --jsonUse the two explicit commands only when the intermediate upload URL is required:
blocks data files presigned-upload-url \
--name invoice.pdf \
--parent-directory-id <directoryId> \
--configuration-name Default \
--access-modifier Private \
--dry-run --json
blocks data files presigned-upload-url \
--name invoice.pdf \
--parent-directory-id <directoryId> \
--configuration-name Default \
--access-modifier Private \
--yes --json
blocks data files upload-to-url \
--url "<uploadUrl>" \
--file ./invoice.pdf \
--content-type application/pdf \
--dry-run --json
blocks data files upload-to-url \
--url "<uploadUrl>" \
--file ./invoice.pdf \
--content-type application/pdf \
--yes --jsonThe presign response contains uploadUrl, fileId, fileVersionId, requiredHeaders, uploadCompletionRequired, and isSuccess. The first call creates the file metadata/version; the PUT fills its object-storage key. Handle PUT failure explicitly because it can leave metadata for missing bytes. Pass --size-in-bytes, --content-type and --checksum (with --checksum-algorithm SHA256|SHA1|MD5) so blocks-data can refuse an oversized file before issuing a URL and verify the bytes later.
Upload completion. When the storage configuration lists the file's access modifier in uploadCompletionRequiredFor (see blocks-storage-configuration), the response says uploadCompletionRequired: true and the bytes land in private quarantine: the file is in the tree but unreadable until completed. Send the requiredHeaders on the PUT, then:
blocks data files complete-upload <fileId> <fileVersionId> --dry-run --json
blocks data files complete-upload <fileId> <fileVersionId> --yes --jsonIt verifies declared size, content type, checksum, and that the real file type matches the extension, then promotes the file (verificationStatus: "Verified") or rejects it with a rejectionReason. It is idempotent. On a version that never needed completion it answers file_version_not_found. data files upload does all of this itself: it declares size/type/SHA-256 checksum, uses the server's headers, completes when required, and fails on a rejection.
Access defaults. --object-access-level Creator|Organization sets who can reach an unshared file (omitted keeps the legacy allow-all default), and --inherits-parent-access=false stops a broad grant on a parent directory from carrying down. Both apply to upload, presigned-upload-url and upload-to-local-storage.
When parentDirectoryId is empty, the cloud upload resolves moduleName to that module's default directory. The backend default is module value 8 (Default_Construct), but pass the intended module or a concrete directory id instead of relying on that default.
Cloud upload from app code
const presign = await blocksClient.data.files.presignedUploadUrl({
name: "invoice.pdf",
parentDirectoryId: directoryId,
configurationName: "Default",
accessModifier: "Private",
tags: "invoice,2026",
});
if (!presign || typeof presign !== "object") {
throw new Error("Unexpected upload response");
}
const result = presign as {
uploadUrl: string;
fileId: string;
isSuccess: boolean;
errors?: Record<string, string>;
};
if (!result.isSuccess) throw new Error(JSON.stringify(result.errors));
await blocksClient.data.files.uploadToUrl({
url: result.uploadUrl,
body: file,
contentType: file.type || "application/octet-stream",
});uploadToUrl is provider-direct and sends no bearer token or x-blocks-key. The SDK adds Azure's x-ms-blob-type: Blockblob header unless overridden; ensure that header matches the signed provider policy (the presign response's requiredHeaders names the ones the provider expects).
The SDK has no upload-completion call yet. If the presign response says uploadCompletionRequired, the file stays quarantined until the upload is completed with its fileId and fileVersionId. Use blocks data files complete-upload from the terminal, or avoid completion-required access modifiers for app-code uploads until the SDK exposes it.
Local-storage upload
blocks data files upload-to-local-storage \
--file ./invoice.pdf \
--parent-directory-id <directoryId> \
--configuration-name Default \
--access-modifier Private \
--dry-run --json
blocks data files upload-to-local-storage \
--file ./invoice.pdf \
--parent-directory-id <directoryId> \
--configuration-name Default \
--access-modifier Private \
--yes --jsonconst uploaded = await blocksClient.data.files.uploadToLocalStorage({
name: file.name,
file,
parentDirectoryId: directoryId,
configurationName: "Default",
accessModifier: "Private",
tags: ["invoice", "2026"],
});This call creates the file object and uploads version 1 in one request. Unlike cloud presign, an empty local parentDirectoryId stays at the top level; it is not resolved through moduleName.
Add a file version
Supplying an existing itemId to either upload flow creates another version after the caller passes the file's Edit check. For cloud storage, the dedicated command/method returns another pre-signed URL:
blocks data files versions <fileId> --limit 25 --json
blocks data files create-version <fileId> --configuration-name Default --dry-run --json
blocks data files create-version <fileId> --configuration-name Default --yes --jsonconst history = await blocksClient.data.files.versions({ fileId, limit: 25 });
const next = await blocksClient.data.files.createVersion({ fileId, configurationName: "Default" });Version history is newest-first and cursor-paginated. Use the returned opaque nextCursor; the current backend uses the last version number internally, but callers must not construct cursors. Limits are 1–100, default 25.
Read and download
blocks data files get <fileId> --configuration-name Default --json
blocks data files get <fileId> --version <versionNo> --configuration-name Default --json
blocks data files get-many <fileId...> --configuration-name Default --jsonconst file = await blocksClient.data.files.get(fileId, {
configurationName: "Default",
version: 2,
});The response includes a download URL plus metadata. Download requires the caller's Download permission. Access-denied reads may deliberately look like missing resources so clients cannot probe hidden object ids.
Directory and object workflows
For browsing, directories, search, move/copy/rename, trash, sharing, and ACL behavior, read flows/object-management.md.
Update custom metadata
blocks data files update-additional-info <fileId> \
--additional-properties '{"status":"reviewed"}' \
--dry-run --json
blocks data files update-additional-info <fileId> \
--additional-properties '{"status":"reviewed"}' \
--yes --jsonThis updates additionalProperties; it does not rename, move, tag, or version the file.
Delete safely
Deletion now distinguishes trash from permanent removal:
permanent: falsearchives the file or directory so it can be restored.permanent: trueremoves it for good.- The backend default is
truewhenpermanentis omitted.
The CLI defaults to safe soft deletion. Add --permanent only after explicit approval.
blocks data files delete <fileId> --dry-run --json
blocks data files delete <fileId> --yes --json
blocks data files delete <fileId> --permanent --dry-run --jsonIn app code, send the choice explicitly: blocksClient.data.files.delete({ fileId, permanent: false }).
Permission model
Every storage request passes two checks:
- The endpoint permission permits that class of action.
- The object ACL permits the action on that specific file/directory.
Capabilities are ordered: View, Download, Edit, Delete, Manage, Owner. Higher capabilities imply lower ones. Directory children inherit ancestor access while inheritsParentAccess is true. New files inherit from their directory.
Do not infer permission from a visible button or endpoint grant. Render actions from each object's returned permissions flags and still handle 403/404 races.
Gotchas
- Upload now creates the visible file object; legacy DMS registration is wrong and may fail after the bytes were successfully PUT.
- Cloud presign creates metadata before the provider PUT. Treat the two steps as a recoverable workflow and surface partial failure.
- A name must contain an allowed extension. Directories may restrict extensions.
- Names are unique within a directory. File move/copy can fail on a name conflict or extension policy.
Privateis the safe default. UsePubliconly when unauthenticated download is intended.configurationNameselects an existing provider record; it does not configure storage.- Most legacy file SDK methods return
Promise<unknown>; validate responses at the boundary. - Never send Blocks auth headers to a pre-signed provider URL.
- Never use a raw API call to work around a missing CLI/SDK wrapper; update the client surface first.
Example triggers
- "Upload this PDF into the Contracts folder."
- "Build a file browser with folders and search."
- "Show files shared with the current user."
- "Move this file to trash and let users restore it."
- "Add version history and upload a replacement version."
- "Share this directory with a role and let its children inherit access."
- "Move, copy, or rename a file."
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

