File Format Specification for Automaitons and MicroApps
A universal, open format for distributing automations and full-stack microapps built around the automations. Runtimes can run on mobile devices, desktops, servers, and containers.
The .habit format delivers portability, cross-platform support, multi-use case flexibility, and auditability, while runtimes provide durability, scalability, consistency, and isolation.
Move the same .habit file between environments without rewriting it.
Run the same package across mobile, desktop, server, and container runtimes.
Deploy as automation, backend API, full-stack SaaS, or standalone microapp: same format.
Standard ZIP with human-readable YAML. Inspect, audit, or modify with any text editor.
Recover after crashes and continue from the last safe point instead of starting over.
Run many executions safely and efficiently as demand grows.
Same inputs produce identical outputs regardless of which runtime or platform executes it.
Each execution runs in its own context. No state leaks between runs or between habits.
This document contains both the normative .habit format specification and .habit runtime rules and non-normative implementation notes for the Cortex reference implementation. Sections are labeled accordingly. The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" are to be interpreted as described in RFC 2119.
The .habit format enables different application types depending on what you include:
The .habit format is intentionally open because the ecosystem depends on it:
The format belongs to the community, not a company. Build on it, extend it, monetize it. it's yours.
The Cortex runtime is the reference implementation for this specification. Open source under Apache 2.0 license on GitHub:
A conforming .habit package and its reader implementation MUST satisfy the following requirements.
stack.yaml file at the archive rootstack.yamlcortex-bundle.js file for JavaScript-based runtimesfrontend/ directory with pre-built UI assetsfrontend-src/ directory with original source fileshabits/ directory with workflow YAML files.env file for embedded secrets (not recommended for distribution)SIGNATURE file for cryptographic signingMANIFEST.json file with metadata and checksumsSIGNATURE file is presentstack.yaml and execute workflows without requiring external downloads after initial runtime setupNote: Individual workflow nodes may require network access (e.g., API calls, webhooks). This requirement applies to the runtime's ability to load and execute the package itself.
Everything needed to run is bundled inside: frontend, backend logic, workflows, and configuration. No external dependencies required at runtime.
Run on phones, tablets, laptops, servers, or serverless platforms. One file works everywhere, from a Raspberry Pi to a Kubernetes cluster.
Signing is an optional format feature. Any compliant implementation MAY support signature verification for publisher identity and file integrity.
Standard ZIP archive containing YAML/JSON configurations. Inspect, audit, or modify the contents with any text editor, no proprietary tools needed.
A .habit file is a standard ZIP archive (PKZIP format) with a specific structure. Implementations MUST be able to extract and parse this archive.
Required only for JavaScript-based runtimes (Node.js, WebView). Contains:
Note: Native runtimes (Rust, Go, etc.) MAY implement the execution engine in their own language and embed all dependencies somehow into the .habit files.
The app's configuration manifest defining:
Optimized for offline execution:
This section defines the precise semantics of the ZIP archive structure.
All paths within the archive MUST use forward slash (/) as the directory separator, regardless of the host operating system.
File and directory names MUST be treated as case-sensitive. stack.yaml and Stack.yaml are distinct files. Packagers SHOULD use lowercase for all required files (stack.yaml, cortex-bundle.js) to ensure cross-platform compatibility.
All text files within the archive MUST be encoded in UTF-8. The archive itself SHOULD use UTF-8 for filenames.
Archives MUST NOT contain duplicate file paths. If duplicates are encountered, the reader SHOULD reject the archive.
Readers SHOULD ignore files and directories not defined in this specification. This allows forward compatibility with future extensions.
Files within the archive MAY use DEFLATE compression. Readers MUST support both compressed and stored (uncompressed) entries.
The stack.yaml file is the primary configuration manifest. It MUST be present at the archive root.
| Field | Type | Required | Description |
|---|---|---|---|
formatVersion | string | MUST | Format/schema version (e.g., "1.0"). Used by runtimes for compatibility checking. |
version | string | SHOULD | Package version (e.g., "2.1.0"). For tracking releases of this specific habit. |
name | string | MUST | Human-readable name of the habit package. |
workflows | array | MUST | Array of workflow definitions. Each entry MUST have id and path. |
workflows[].id | string | MUST | Unique identifier for the workflow within this package. |
workflows[].path | string | MUST | Relative path to the workflow YAML file. |
server | object | SHOULD | Server configuration options. |
server.frontend | string | SHOULD | Path to frontend directory (defaults to ./frontend). |
server.port | integer | MAY | Default port for the server (defaults to 3000). |
description | string | MAY | Human-readable description of the package purpose. |
author | string | MAY | Package author name or organization. |
logging | object | MAY | Logging configuration options. |
Workflow files define the automation logic. They are YAML files referenced by stack.yaml and typically stored in a habits/ directory.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | MUST | Unique identifier for the workflow. Used in API routes and references. |
name | string | SHOULD | Human-readable name displayed in UIs. |
description | string | MAY | Description of what this workflow does. |
input | array | MAY | Input parameters the workflow accepts. Each entry has id, type, required, and description. |
nodes | array | MUST | Array of node definitions. Each node represents a step in the workflow. |
edges | array | MAY | Connections between nodes. If omitted, nodes execute sequentially. |
output | object | SHOULD | Maps workflow outputs to node results using template syntax. |
Each node in the nodes array defines a workflow step:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | MUST | Unique identifier within the workflow. Referenced in templates and edges. |
type | string | MUST | Node type: bits, script, trigger, action, etc. |
data | object | MUST | Node configuration including module, operation, params, and credentials. |
data.framework | string | SHOULD | Framework: bits, activepieces, n8n, or script. |
data.module | string | SHOULD | Module/package name (e.g., @ha-bits/bit-http). |
data.source | string | SHOULD | Where to load the module: npm, local, github, or link. |
data.operation | string | SHOULD | The operation to perform (module-specific). |
data.params | object | MAY | Parameters passed to the operation. Supports template syntax. |
data.credentials | object | MAY | Credential references using environment variables or secrets. |
# habits/get-posts.yaml
id: get-posts
name: Get Recent Posts
description: Retrieve all published posts from the database
input:
- id: limit
type: number
required: false
description: Maximum number of posts to return
nodes:
- id: query-posts
type: bits
data:
framework: bits
source: npm
module: "@ha-bits/bit-database"
resource: query
operation: query
params:
collection: "posts"
filter: '{"status": "published"}'
limit: "{{habits.input.limit}}"
edges: []
output:
posts: "{{query-posts.results}}"
count: "{{query-posts.count}}"Workflows use double-brace templates to reference values:
{{habits.input.paramName}}: Reference workflow input parameters{{habits.env.ENV_VAR}}: Reference environment variables or secrets{{nodeId.fieldName}}: Reference output from a previous nodeWorkflows support multiple node types for different use cases:
The primary node type for integrations. Uses the bits framework with modular packages:
- id: generate-content
type: bits
data:
framework: bits
source: npm
module: "@ha-bits/bit-openai"
operation: ask_chatgpt
credentials:
openai:
apiKey: "{{habits.env.OPENAI_API_KEY}}"
params:
model: gpt-4o
temperature: 0.7
maxTokens: 1000
prompt: "Write a summary about: {{habits.input.topic}}"Execute custom code in multiple languages (TypeScript/Deno, Python, Bash, Go):
- id: process-data
type: script
data:
framework: script
source: inline
params:
input: "{{previous-node.output}}"
language: deno
script: |
export async function main(input: string) {
const processed = input.toUpperCase();
return { result: processed, length: processed.length };
}Support for activepieces and n8n node types is deprecated and will be removed in a future version. Please migrate to bits nodes. The examples below are provided for legacy compatibility only.
Legacy support for Activepieces modules:
- id: generate-text
type: activepieces
data:
framework: activepieces
source: npm
module: "@activepieces/piece-openai"
operation: ask_chatgpt
credentials:
apiKey: "{{habits.env.OPENAI_API_KEY}}"
params:
prompt: "Write a motivational quote about: {{habits.input.prompt}}"
model: gpt-4o-miniLegacy support for n8n modules:
- id: text-to-speech
type: n8n
data:
framework: n8n
source: npm
module: n8n-nodes-elevenlabs
operation: text-to-speech
credentials:
elevenLabsApi:
xiApiKey: "{{habits.env.ELEVENLABS_API_KEY}}"
params:
resource: speech
text: "{{generate-text}}"
voice_id: 21m00Tcm4TlvDq8ikWAMThis section defines how version numbers are interpreted and how readers should handle version mismatches.
The version field in stack.yaml MUST follow semantic versioning format: MAJOR.MINOR (e.g., "1.0", "2.1").
A change in the major version indicates breaking changes. Readers MUST NOT attempt to execute packages with a higher major version than they support.
A change in the minor version indicates backwards-compatible additions. Readers SHOULD support packages with the same major version but any minor version.
Readers SHOULD ignore unknown fields in stack.yaml to maintain forward compatibility with newer minor versions.
Signing is an optional format feature. Compliant implementations MAY support signature verification for publisher identity and file integrity.
Publisher creates a digital signature using their private key. This cryptographically binds their identity to the file contents.
The .habit file includes the signature and publisher's public key (or key ID in case of using an enterprise vault) for verification.
Runtime validates signature against publisher's public key. Tampering is detected, untrusted sources are flagged.
Compliant implementations MUST support at least one of the following secret resolution profiles. Secrets MAY be required by workflows for API keys, passwords, or other sensitive data.
Embed a .env file directly inside the .habit archive. Secrets are bundled with the package.
Place a .env file in the same directory as the .habit file. The runtime automatically loads it.
/opt/habits/
βββ my-app.habit
βββ .env β Secrets loaded from here Secrets are stored in the operating system's secure keychain. Users enter credentials once, and they're securely persisted.
| Method | Server / CLI | Docker | Serverless | Mobile | Desktop | Distributable |
|---|---|---|---|---|---|---|
.env inside .habit | ||||||
.env beside .habit | ||||||
| OS Keyring |
This section describes common deployment targets for .habit packages. Runtime implementations may support any subset of these profiles.
npx @ha-bits/cortexPlatforms marked with First-Run Setup require a one-time installation of the Cortex runtime. You can either let it do the installations on the first request/run. After the initial setup, the habit will run everytime without any installations.
In case you want to do the installation manually instead of the first run:
npx @ha-bits/cortex install --config ./my-app.habitThis command installs the bits needed and their dependencies, this is recommended for containers and serverless functions.
The .habit format is an open specification governed by the following policies.
This specification is released under the Apache 2.0 License. Anyone may implement .habit readers/writers without royalty or restriction.
Custom fields in stack.yaml MUST use a vendor prefix (e.g., x-mycompany-*). Unprefixed fields are reserved for future specification use.
The following file names at the archive root are reserved: stack.yaml, cortex-bundle.js, SIGNATURE, MANIFEST.json, LICENSE, README.md.
The following sections are non-normative and describe the Cortex reference implementation.
This appendix describes how the Cortex reference implementation runs .habit files. Other runtimes may implement different approaches.
.env file for secretsRun by downloading the app: no Node.js, npm, or terminal needed.
Home Screen
Add New Habit
Set SecretsRequires Node.js 24+. Runs as a background server exposing HTTP API.
Automation Frontend (If provided)
Automation Monitoring
API DocumentationCompliant viewers SHOULD be able to display each workflow in the package independently.
Home Screen
Workflow Options
View OutputScreenshot: Browsing and inspecting habits in the Base web interface

First, create a regular stack using one of these methods:
Visual editor with drag-and-drop workflow builder
Describe what you want and let AI create the habit
Write YAML/JSON directly in your favorite editor
You can use Habits Base or just write the stack.yaml, the habit files in yaml and frontends.
# stack.yaml
version: "1.0"
name: "my-automation"
workflows:
- id: main
path: ./habits/main.yaml
server:
frontend: ./frontend
port: 3000$ npx habits pack --format habit --config ./stack.yaml