Publish to Hub — Functions
The Aerostack Hub is the community marketplace for Functions. Publish your Function and other developers can install it with one command. There’s no buying or selling — the Hub is free.
Why Publish?
Section titled “Why Publish?”- Share your work — other developers can install your Function into their projects as source code
- Build reputation — earn Community Reputation points that increase your visibility in search
- Get installs — your Function appears in the Hub search and CLI discovery
- Help the ecosystem — every Function makes Aerostack more powerful for everyone
What Can You Publish?
Section titled “What Can You Publish?”Any Function that solves a reusable problem:
| Category | Examples |
|---|---|
auth | OAuth handlers, JWT utilities, rate limiters |
payments | Stripe checkout, invoice generators, subscription managers |
ai | Text summarizers, sentiment analyzers, embedding generators |
media | Image resizers, PDF generators, video processors |
email | Template renderers, SMTP senders, newsletter managers |
database | Migration helpers, seed scripts, query builders |
utility | Slug generators, ID generators, validators |
analytics | Event trackers, dashboard aggregators, report generators |
notifications | Push notifications, Slack/Discord alerts, SMS senders |
storage | File upload handlers, CDN managers, backup utilities |
Function Structure
Section titled “Function Structure”Every publishable Function needs an aerostack.json config and at least one source file.
Directorymy-function/
- aerostack.json // Required — metadata and config
- index.ts // Required — your function logic
- README.md // Recommended — shown on the Hub page
The aerostack.json file
Section titled “The aerostack.json file”This defines your Function’s identity, version, and how it should be installed.
{ "name": "smart-rate-limiter", "version": "1.0.0", "description": "AI-powered rate limiter that adapts limits based on request patterns.", "category": "auth", "language": "typescript", "runtime": "cloudflare-worker", "license": "MIT", "tags": ["rate-limit", "security", "ai", "adaptive"], "entrypoint": "index.ts", "routePath": "/api/rate-limit", "routeExport": "rateLimiterRoute", "npmDependencies": [], "envVars": ["RATE_LIMIT_MAX", "RATE_LIMIT_WINDOW"], "drizzleSchema": false}Field reference
Section titled “Field reference”| Field | Required | Description |
|---|---|---|
name | Yes | Function name — becomes the slug in the Hub URL |
version | Yes | SemVer, e.g. 1.0.0. Bump on each publish |
description | Yes | Short description shown in search results |
category | Yes | One of: auth, payments, media, email, ai, database, utility, analytics, notifications, storage |
language | Yes | typescript or javascript |
runtime | Yes | cloudflare-worker |
license | Yes | SPDX identifier: MIT, Apache-2.0, etc. |
tags | No | Search keywords, max 10 |
entrypoint | Yes | Path to main source file |
routePath | No | Default HTTP route when installed (e.g. /api/rate-limit) |
routeExport | No | Named Hono export from your file |
npmDependencies | No | npm packages the consumer must install |
envVars | No | Environment variables the consumer must set |
drizzleSchema | No | true if you export a Drizzle schema |
Writing a Publishable Function
Section titled “Writing a Publishable Function”Export a Hono route handler. This is the convention the CLI uses when installing.
import { Hono } from 'hono'
// Named export — matches "routeExport" in aerostack.jsonexport const rateLimiterRoute = new Hono<{ Bindings: { CACHE: Cache } }>()
rateLimiterRoute.post('/check', async (c) => { const { ip, endpoint } = await c.req.json<{ ip: string; endpoint: string }>() const key = `ratelimit:${ip}:${endpoint}`
const current = parseInt(await c.env.CACHE.get(key) || '0') const limit = parseInt(c.env.RATE_LIMIT_MAX || '100')
if (current >= limit) { return c.json({ allowed: false, remaining: 0, resetIn: 60 }, 429) }
await c.env.CACHE.put(key, String(current + 1), { expirationTtl: 60 })
return c.json({ allowed: true, remaining: limit - current - 1 })})
rateLimiterRoute.get('/health', (c) => c.json({ module: 'smart-rate-limiter', ok: true }))Publishing Methods
Section titled “Publishing Methods”The CLI is the fastest way to publish.
-
Push to create a draft
Terminal window aerostack functions push ./my-function/index.tsOutput:
Pushing function 'smart-rate-limiter' to Aerostack...Pushed successfully!Slug: smart-rate-limiterStatus: draftAdmin URL: https://app.aerostack.dev/functions/edit/<id> -
Review in Admin (optional)
Open the Admin URL to add a README, adjust tags, and preview how it looks on the Hub.
-
Publish
Terminal window aerostack functions publish <id>Your Function is now live on the Hub.
Auto-publish on every push to main.
-
Add your API key as a GitHub secret
Go to Settings → Secrets → Actions → create
AEROSTACK_API_KEYwith your account key (ac_secret_xxxx). -
Add the workflow
.github/workflows/publish-aerostack.yml name: Publish to Aerostack Hubon:push:branches: [main]jobs:publish:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: '20'- name: Read config and publishrun: |CONFIG=$(cat aerostack.json | tr -d '\n')ENTRYPOINT=$(echo "$CONFIG" | jq -r '.entrypoint // "index.ts"')CODE=$(cat "$ENTRYPOINT" | jq -Rs .)PAYLOAD=$(jq -n \--argjson config "$CONFIG" \--argjson code "$CODE" \'$config + {code: $code, publish: true}')curl -sf \-X POST https://api.aerostack.dev/api/community/functions/ci/publish \-H "X-API-Key: ${{ secrets.AEROSTACK_API_KEY }}" \-H "Content-Type: application/json" \-d "$PAYLOAD" | jq .
The CI endpoint upserts by slug — same name updates the existing Function instead of creating a new one.
Auto-publish from GitLab.
-
Add your API key as a CI variable
Settings → CI/CD → Variables → add
AEROSTACK_API_KEY. -
Add the pipeline
.gitlab-ci.yml stages:- publishpublish-to-aerostack:stage: publishimage: node:20-alpinebefore_script:- apk add --no-cache curl jqscript:- |CONFIG=$(cat aerostack.json)ENTRYPOINT=$(echo "$CONFIG" | jq -r '.entrypoint // "index.ts"')CODE=$(cat "$ENTRYPOINT" | jq -Rs .)PAYLOAD=$(jq -n --argjson config "$CONFIG" --argjson code "$CODE" '$config + {code: $code, publish: true}')curl -sf -X POST "https://api.aerostack.dev/api/community/functions/ci/publish" \-H "X-API-Key: $AEROSTACK_API_KEY" \-H "Content-Type: application/json" \-d "$PAYLOAD" | jq .only:- main
Publish directly from the browser — no local tools needed.
-
Go to My Functions
Log in to app.aerostack.dev → My Functions → Create.
-
Write your code
Use the built-in Monaco editor. Add a name, description, category, and tags.
-
Publish
Click Publish. Your Function is live on the Hub.
How Others Install Your Function
Section titled “How Others Install Your Function”Once published, any developer can install your Function:
# Install by slugaerostack functions install smart-rate-limiter
# Install by username/slugaerostack functions install alice/smart-rate-limiterWhat happens when someone installs:
- The CLI downloads your source code
- Creates
src/modules/smart-rate-limiter/in their project - Auto-registers the route in their
aerostack.toml - Auto-imports the route in their
src/index.ts - Prompts to install npm dependencies (if any)
- Prompts to set environment variables (if any)
The consumer owns the code. It’s copied as source — not linked as a dependency.
Versioning
Section titled “Versioning”Every time you publish, a version snapshot is created. Past versions are preserved — consumers who installed an earlier version keep their code.
- Bump
versioninaerostack.jsonbefore each publish - You cannot overwrite a published version
- Use SemVer:
1.0.0→1.0.1(patch),1.1.0(minor),2.0.0(breaking)
Reputation
Section titled “Reputation”Publishing and getting installs earns Community Reputation:
| Action | Points |
|---|---|
| Publish a Function | +10 |
| Someone installs your Function | +5 |
| Someone stars your Function | +2 |
| Someone clones/forks your Function | +5 |
Higher reputation = better visibility in Hub search results and the developer leaderboard.
Common Errors
Section titled “Common Errors”| Error | Fix |
|---|---|
INVALID_CATEGORY | Check spelling, use lowercase |
MISSING_ENTRYPOINT | Verify entrypoint path in aerostack.json |
EMPTY_CODE | Entrypoint file is empty |
DUPLICATE_SLUG | Name already taken by another account — rename your Function |
UNAUTHORIZED | API key invalid — run aerostack login again |
VERSION_CONFLICT | Bump version in aerostack.json |