Template storage
A template store keeps your team’s PowerPoint templates by name, with every version uploaded. Everything in Astlide can use the same store:
- the render service: callers render with
"template": "acme"instead of uploading the file every time - your Astro site: every stored template becomes a theme (Use stored templates in a site)
- the command line:
astlide template add,list,remove… (Manage templates from the command line) - coding agents, through the MCP server (Let agents see the templates)
- your own code, through
@astlide/core/templates
So a designer can upload a new version of the company template once, and the next render, build and agent all pick it up. This page covers where the templates live, how to give access to them, and each way of using them.
Choose where templates live
Section titled “Choose where templates live”A store is named by its location. Give it in ASTLIDE_TEMPLATE_STORE (the render service, the CLI and the MCP server read it), with --store on the command line, or as templates in astro.config.mjs. The prefix after the bucket is optional; use it to keep the templates in one part of a bucket you share with other things.
| Location | Storage |
|---|---|
/data/templates |
A directory on the server |
s3://<bucket>/<prefix> |
Amazon S3, or a service with an S3-compatible API such as Cloudflare R2 or MinIO |
gs://<bucket>/<prefix> |
Google Cloud Storage |
azure://<container>/<prefix> |
Azure Blob Storage |
A directory is the simplest choice for a single server. Pick a bucket when the service runs in containers that come and go, or when several servers render with the same templates.
Create the bucket or container first; Astlide doesn’t create one. The render service lists the store once when it starts, and the site reads it when it builds or its dev server starts. Either stops with the error if it can’t reach the store or isn’t allowed in, so a wrong setting shows up right away rather than on the first upload.
Astlide talks to each cloud through the provider’s official SDK, so it finds credentials the same way the provider’s own tools do: an IAM role, an attached service account or a managed identity works without any key in your configuration. The variables involved are never passed to the build and browser processes that render slides.
The Docker image and @astlide/server include the SDKs. When you use the store from your own code, install the one you need (see Use the store from your own code).
A directory
Section titled “A directory”Point ASTLIDE_TEMPLATE_STORE at a directory. The server creates it if it’s missing. In Docker, mount a volume there, or the templates disappear with the container. The image has /data/templates ready for this:
docker run --rm -p 8787:8787 -e ASTLIDE_API_KEY \ -e ASTLIDE_TEMPLATE_STORE=/data/templates -v astlide-templates:/data/templates \ --security-opt seccomp=packages/server/chromium-seccomp.json astlide-serverAmazon S3
Section titled “Amazon S3”-e ASTLIDE_TEMPLATE_STORE=s3://acme-assets/astlide/templates -e AWS_REGION=ap-northeast-1Credentials come from the AWS SDK’s default chain. On AWS, give the service a role and set no keys at all: an ECS task role, an EC2 instance profile, or an EKS service account (IRSA or Pod Identity). Elsewhere, set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (and AWS_SESSION_TOKEN for temporary credentials), or mount a shared config file and choose a profile with AWS_PROFILE.
The role or user needs to list the prefix and to read, write and delete the objects under it:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::acme-assets", "Condition": { "StringLike": { "s3:prefix": "astlide/templates/*" } } }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::acme-assets/astlide/templates/*" } ]}Cloudflare R2, MinIO and other S3-compatible services
Section titled “Cloudflare R2, MinIO and other S3-compatible services”Use an s3:// store and set the service’s URL in AWS_ENDPOINT_URL_S3. With an endpoint set, Astlide puts the bucket in the URL path, which these services expect, and uses us-east-1 unless you set a region.
# Cloudflare R2: an API token with Object Read & Write on the bucket-e ASTLIDE_TEMPLATE_STORE=s3://astlide-templates \-e AWS_ENDPOINT_URL_S3=https://<account-id>.r2.cloudflarestorage.com -e AWS_REGION=auto \-e AWS_ACCESS_KEY_ID=<access key id> -e AWS_SECRET_ACCESS_KEY=<secret access key>
# MinIO-e ASTLIDE_TEMPLATE_STORE=s3://astlide-templates \-e AWS_ENDPOINT_URL_S3=http://minio:9000 \-e AWS_ACCESS_KEY_ID=<access key> -e AWS_SECRET_ACCESS_KEY=<secret key>Google Cloud Storage
Section titled “Google Cloud Storage”-e ASTLIDE_TEMPLATE_STORE=gs://acme-assets/astlide/templatesCredentials come from Application Default Credentials. On Cloud Run, GKE (with Workload Identity) and Compute Engine, the service account attached to the service is used, and nothing else needs setting. Elsewhere, create a key for a service account, mount the JSON file and point GOOGLE_APPLICATION_CREDENTIALS at it:
docker run … \ -e ASTLIDE_TEMPLATE_STORE=gs://acme-assets/astlide/templates \ -e GOOGLE_APPLICATION_CREDENTIALS=/secrets/astlide-sa.json \ -v "$PWD/astlide-sa.json:/secrets/astlide-sa.json:ro" astlide-serverOn your own machine, gcloud auth application-default login is enough.
Give the service account the Storage Object User role on the bucket. It covers reading, writing, deleting and listing objects:
gcloud storage buckets add-iam-policy-binding gs://acme-assets \ --member=serviceAccount:astlide@<project>.iam.gserviceaccount.com \ --role=roles/storage.objectUserTo try things against an emulator such as fake-gcs-server, set STORAGE_EMULATOR_HOST to its URL.
Azure Blob Storage
Section titled “Azure Blob Storage”-e ASTLIDE_TEMPLATE_STORE=azure://templates/astlide -e AZURE_STORAGE_ACCOUNT=acmeassetsThe first part after azure:// is the container. Astlide signs in with the first of these that is set:
| Set | Sign-in |
|---|---|
AZURE_STORAGE_CONNECTION_STRING |
The account’s connection string (from the portal’s Access keys page). AZURE_STORAGE_ACCOUNT isn’t needed. |
AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_KEY |
The account name and one of its access keys. |
AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_SAS_TOKEN |
A SAS token for the container, with read, create, write, delete and list permissions (rcwdl). |
AZURE_STORAGE_ACCOUNT only |
Microsoft Entra ID, through DefaultAzureCredential. |
Entra ID avoids keys altogether. On Azure Container Apps, App Service, AKS (with workload identity) and virtual machines, the managed identity is used. Elsewhere, set a service principal in AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET, or sign in with az login on your own machine. Give the identity the Storage Blob Data Contributor role on the container:
az role assignment create --assignee <identity's client id> \ --role "Storage Blob Data Contributor" \ --scope /subscriptions/<sub>/resourceGroups/<group>/providers/Microsoft.Storage/storageAccounts/acmeassets/blobServices/default/containers/templatesFor Azurite or a sovereign cloud, set the blob endpoint in AZURE_STORAGE_BLOB_ENDPOINT (for Azurite, http://127.0.0.1:10000/devstoreaccount1).
Manage templates from the command line
Section titled “Manage templates from the command line”astlide template adds, lists, inspects and removes templates. It works on the store in ASTLIDE_TEMPLATE_STORE, or the one you name with --store, with the same credentials as above:
export ASTLIDE_TEMPLATE_STORE=gs://acme-assets/astlide/templates
bunx astlide template add "Acme 2026.potx" --label "Acme 2026" # → acme-2026 v1bunx astlide template add acme-new.potx --name acme-2026 # → acme-2026 v2bunx astlide template listbunx astlide template show acme-2026 # versions, and the slideLayout namesbunx astlide template download acme-2026 --version 1 -o old.potxbunx astlide template remove acme-2026 --version 1The name comes from the file name unless you give --name. add reads the file first and refuses one that isn’t a PowerPoint template. list and show print JSON with --json.
Use stored templates in a site
Section titled “Use stored templates in a site”Set the templates option to the store’s location instead of a list of files. Every template in the store becomes a theme of the same name, in its latest version:
astlide({ templates: "s3://acme-assets/astlide/templates" }){ "title": "Q3 review", "theme": "acme-2026" }A relative folder ("./templates") is resolved from the project. The store is read when the site builds and when the dev server starts. With a folder, the dev server also restarts by itself when a template is added, replaced or removed; with a bucket, restart it to pick up a new version. The cloud SDK must be installed in the project (see below).
Let agents see the templates
Section titled “Let agents see the templates”Start the MCP server with the store, and agents can find the team’s templates on their own:
claude mcp add astlide -- bunx astlide mcp --template-store s3://acme-assets/astlide/templatesThis adds a read-only list_templates tool, and template_layouts takes a stored template’s name (template: "acme-2026") as well as a file. Agents can’t add or remove templates. To render with one, an agent sets the deck’s theme to its name; that works when the project’s templates option names the same store. See Agents for the rest of the MCP server.
Use the store from your own code
Section titled “Use the store from your own code”@astlide/core/templates is the store the render service uses. openTemplateStore() takes the same values as ASTLIDE_TEMPLATE_STORE:
import { openTemplateStore } from "@astlide/core/templates";
const store = openTemplateStore("gs://acme-assets/astlide/templates");await store.put("acme", await readFile("acme.potx"), { ext: ".potx", label: "Acme 2026" });await store.list(); // every template, with its versionsawait store.get("acme"); // the latest version and its fileawait store.get("acme", 1); // version 1await store.remove("acme"); // all versionsInstall the SDK for the storage you use: bun add @aws-sdk/client-s3, bun add @google-cloud/storage, or bun add @azure/storage-blob (plus @azure/identity to sign in with Entra ID). If it’s missing, the store says which package to add.
To pass your own client, for example one with a custom retry policy, create the store directly:
import { S3Client } from "@aws-sdk/client-s3";import { createS3TemplateStore } from "@astlide/core/templates";
const store = createS3TemplateStore({ bucket: "acme-assets", prefix: "astlide/templates", client: new S3Client({ region: "ap-northeast-1", maxAttempts: 5 }),});createGcsTemplateStore({ bucket, prefix, storage }) and createAzureBlobTemplateStore({ container, prefix, client }) work the same way.
For another storage service, implement ObjectStorage. It reads, writes, deletes and lists objects by key, and createObjectTemplateStore() builds the versioning on top:
import { createObjectTemplateStore, type ObjectStorage } from "@astlide/core/templates";
const storage: ObjectStorage = { read: async (key) => /* the object's bytes, or undefined if it doesn't exist */, write: async (key, data) => { /* store the whole object at once */ }, delete: async (key) => { /* remove it; a missing object is fine */ }, list: async (prefix) => /* every key starting with prefix */,};const store = createObjectTemplateStore(storage);How templates are kept
Section titled “How templates are kept”Under the prefix, each template has a key prefix of its own name, holding manifest.json (its label and versions) and one file per version as uploaded:
astlide/templates/├── acme/│ ├── manifest.json│ ├── v1.potx│ └── v3.potx└── brand/ ├── manifest.json └── v1.pptxTo back the templates up or move them to another storage, copy everything under the prefix. Change templates through the API rather than by editing these files.
Any number of servers can render from one store. Let only one of them add or delete templates, though: a store has no lock across servers, and two uploads to the same template at the same moment can overwrite each other’s manifest.