Core Concepts
Before diving into specific features, here are the fundamental ideas that make everything in Kitcord click.
📄The Bot Kit
A Bot Kit is the single source of truth for your bot. It's a JSON manifest that describes everything — commands, events, flows, and database schema. The Canvas writes it; the Engine runs it.
When you publish, Kitcord serializes your Canvas into a Kit, stores it, and signals the Engine to go live. The Engine reads the Kit and your bot connects to Discord. That's the whole loop.
🧩Pieces
A Piece is a single, atomic capability — one thing a bot can do. Send a message. Check a condition. Query the database.
🔗Flows
A Flow is a directed graph of Piece nodes. When a Spell or Trigger fires, the Engine starts at the entry node and walks forward until it hits a terminal node.
// A Flow is just an async function that runs when a Spell or Trigger fires:
async function flow_ping(interaction: ChatInputCommandInteraction) {
// node_reply → base.reply:
await interaction.reply({ content: "Pong!" });
}
// A branching flow with conditions:
async function flow_welcome(member: GuildMember) {
const rows = await db.all("SELECT * FROM members WHERE user_id = ?", [member.id]);
if (rows.length > 0) { // base.condition → true
await channel.send("Welcome back!");
} else { // base.condition → false
await channel.send("Hey, welcome! 🎉");
await db.run("INSERT INTO members (user_id) VALUES (?)", [member.id]);
}
}✨Sparks
Sparks are dynamic values you inject into any Piece config using {{path}} syntax. They're the glue between Discord data and your flow logic.
{{input.user.id}}Spell runner's Discord ID{{input.user.name}}Their username{{message.content}}Message text (messageCreate trigger){{input.options.amount}}A spell input named 'amount'{{score}}A spark saved by Save Spark upstream{{lib.filter.profanity}}Pack library spark (Naughty List)// Sparks are just variables in code.
// {{input.user.id}} → const userId = interaction.user.id
// {{input.user.name}} → const username = interaction.user.username
// {{score}} → let score = 0; // set by Save Spark earlier in the flow
// In a config field: "Hello, {{input.user.name}}!"
// In code: `Hello, ${interaction.user.username}!`🪄⚡Spells vs Triggers
Slash commands users invoke with /command.
- • User-triggered, on demand
- • Accept typed inputs (options)
- • Can be permission-gated
Discord events that fire automatically when something happens.
- • Automatic, event-driven
- • Joins, leaves, messages, reactions…
- • Can be filtered (ignore bots, etc.)
🗄️The Vault
Every bot gets an isolated SQLite database — the Vault. Define the schema in your Kit; the Engine creates or migrates it on every deploy.
Access it from any flow via base.db.* Pieces. The database is safe and isolated — it lives on the Engine server, not in the Kit JSON.
// The Vault is just a SQLite database:
await db.run(`
CREATE TABLE IF NOT EXISTS scores (
user_id TEXT PRIMARY KEY,
username TEXT DEFAULT '',
points INTEGER DEFAULT 0
)
`);
// Write to Vault (with upsert) saves a row:
await db.run("INSERT INTO scores ... ON CONFLICT DO UPDATE SET points = points + 1", [userId]);
// Read from Vault loads rows into {{result}}:
const rows = await db.all("SELECT * FROM scores ORDER BY points DESC LIMIT 10");📦Expansions
Every bot ships with the Base Set. Expansions are free add-on packs that unlock new categories of Pieces.
All expansions are free. Unbox them into your account, install on specific bots. New Pieces appear in the Canvas palette immediately.
Expansions docs →📦Export & Self-Hosting
Any bot exports as a ZIP — kit.json, a SQLite snapshot, a Mini Engine binary, and a README. Run it on any machine. Free forever.