Product help

How appshare fits together.

appshare lets a team publish mini apps as static frontends while the server provides the shared project services: data, files, business functions, audit, policies, SDKs, MCP, and controlled app.

The short version

Build the UI with the Appshare SDK, keep sensitive business logic in Project Functions, publish to dev first, then promote to prod when the project owner is ready.

ConceptsProjects, folders, environments, roles. BuildBootstrap lane, React lane, SDKs. SecurityRuntime policies, PII audit, secrets. AgentsCodex, MCP, generated SDKs.

01

Core concepts

Project

The ownership boundary. Tables, storage, functions, policies, keys, logs, webhooks, and apps belong to a project.

Environment

dev, test, and prod isolate data and services. Codex should use dev by default.

Folder

A published app URL under /f/{folder}/. One project can publish multiple folders that share the same project services.

Runtime profile

A business role such as admin, guest, or legal. It controls what the running app can do.

02

Recommended workflow

  1. Create or select a project in the console.
  2. Build the mini app using the Appshare SDK rather than raw REST calls.
  3. Write sensitive or identity-dependent business logic as a Project Function.
  4. Publish the folder to dev and test with realistic server data.
  5. Configure runtime policy, folder visibility, and allowed runtime profiles.
  6. Promote explicitly to prod when the app is ready.

Do not use browser storage as the source of truth for business data. Use project tables, project storage, or functions.

03

Managed services

Tables

Project tables store JSON rows per environment. Schemas can validate shape, generate SDK types, and mark PII/privacy metadata.

Storage

Project storage keeps files separate from published app assets. Buckets are environment-scoped and controlled by runtime policy.

Project Functions

Functions are small server-side JavaScript handlers for business logic. They receive a safe ctx and cannot access Node APIs, filesystem, network, or secrets directly.

Email, LLM, jobs, and webhooks

Email and LLM calls are logged and quota-controlled. Jobs handle async work such as imports/exports. Webhooks emit signed events with delivery history.

To bulk-load rows into a table, use a table_import job rather than looping create_table_row: it writes the whole batch in one store write and one SQLite transaction. Pass the data inline as source.content (JSON or CSV, payload up to 5MB) or from a storage object via source.bucket/source.key (up to 10MB). To load several tables from one upload, use a table_import_zip job pointing at an uploaded zip that contains a manifest.json (a tables array of file/table/mapping entries) plus the data files. Pass wait: true to run an import job inline and get the finished result in one call instead of polling.

To read from an external API (e.g. Personio), an admin sets up a connector that holds the credential once and grants access per person or role. Apps call it live with appshare.connector("personio").call("employees") (the token stays on the server), or sync it into a table with a table_import job using source.connector/source.module. You never put the API token in the app or in per-project secrets.

04

Frontend lanes

Bootstrap lane

Use HTML, vanilla JS, Bootstrap, /sdk/appshare.js, /sdk/appshare-ui.js, and /sdk/appshare-icons.js. This is the default for CRUDs, dashboards, forms, and master-detail modals.

React lane

Use React only for advanced state, nested editors, rich interaction, or reusable components. Keep the stack closed and use the Appshare React Kit.

const appshare = Appshare.create({
  projectId: "prj_...",
  environment: "dev"
});

const rows = await appshare.table("orders").list({ limit: 50 });
const result = await appshare.function("mis_incidencias_pendientes").invoke({ limit: 20 });

Icons

Appshare self-hosts a curated icon set (about 100 icons, the same Lucide geometry the console uses), so apps do not need an icon CDN. Load /sdk/appshare-icons.js and use the <as-icon> element or a data-icon placeholder; icons render inline, take the current text colour and size with the surrounding text.

<script src="/sdk/appshare-icons.js"></script>

<button class="btn btn-dark"><as-icon name="plus"></as-icon> New task</button>
<button class="btn btn-outline-dark btn-sm" aria-label="Refresh"><i data-icon="refresh-cw"></i></button>

// when building HTML strings
row.innerHTML = AppshareIcons.markup("trash-2", { label: "Delete" });

The full name list is at /sdk/appshare-icons.svg (one <symbol> per icon). Names are Lucide names in kebab-case: search, trash-2, refresh-cw, triangle-alert, chart-column… If an icon you need is missing, ask for it: the set is generated by scripts/gen_appshare_icons.mjs and adding a name is a one-line change.

05

Security model

Administrative roles

Project owner, editor, and viewer control who can manage the project from the console and APIs.

Runtime policies

Runtime policies control what the app can do at runtime: list/view/create/edit/delete table rows, invoke functions, call email/LLM, create jobs, access storage, or trigger webhooks.

Folder profile gates

A folder can set allowed_runtime_roles. For example, a public folder may still be available only to profile legal.

PII and privacy levels

Table schemas can mark fields with pii, x-pii, privacy_level, and pii_category. When a response exposes PII, appshare writes an audit log of who accessed which field classes.

Secrets

Secrets are stored server-side and returned only as redacted metadata. Browser apps, generated SDKs, REST responses, and MCP tools must not expose secret values.

06

Agents and MCP

Codex and other agents should use project-aware MCP tools such as publish_app, promote_project_app, upsert_project_function, create_project_job, and generate_project_sdk.

Use LLM instructions for machine-readable guidance. Use this page when a human needs the product model.

Project API keys use the hpk_... prefix, are scoped to one project, and should include only the scopes needed by the agent or integration.

07

Example: pending incidents

If an app needs "my pending incidents", do not download all incidents to the browser and filter there. Create a Project Function that receives ctx.user, reads the allowed rows server-side, and returns only the safe result.

The repository includes a complete reference app at examples/pending-incidents/ with frontend files, Project Functions, schema, runtime policy, seed data, publish payload, and import job mapping. The public gallery is available at /examples.

function handler(event, ctx) {
  return ctx.tables.list("incidents", {
    where: { assignee_email: ctx.user.email, status: "pending" },
    limit: event.limit || 50
  });
}