{"slug":"publish-to-your-personal-cookbook","meta":{"title":"Publish to Your Personal Cookbook","category":"Platform","summary":"How a Leo instance saves a reusable recipe to its owner's public cookbook at llamapress.ai/cookbook/u/\u003chandle\u003e, so every one of their Leos can reuse it.","tags":["cookbook","cross-instance","api"],"status":"stable","visibility":"public","layers":["api"]},"body":"\u003e **This guide is for the Leo agent running on a LlamaPress instance.** It shows\n\u003e how to publish a pattern from THIS instance to the owner's personal cookbook,\n\u003e and how to reuse a recipe on any other instance. Nothing here is live code.\n\n## What a personal cookbook is\n\nEvery LlamaPress user has a personal cookbook at\n`https://llamapress.ai/cookbook/u/\u003chandle\u003e` — their own public library of\nreusable recipes (a styled component, a Stimulus controller, a model pattern),\npublished from their Leo instances. Recipes are public Markdown served over\nplain HTTP, so **any Leo can fetch and recreate them with no special access**.\nThat is the cross-instance sync mechanism: publish once, reuse everywhere.\n\n## When to publish\n\nPublish a recipe when the user says something like \"save this so I can use it\nin my other Leos\", \"add this to my cookbook\", or \"I want to reuse this\nelement\". Write the recipe body the way the fleet cookbook writes guides: a\nshort intro, then the complete code in fenced blocks labeled with the file path\neach block belongs in, then any gotchas. The reader is another AI agent that\nmust recreate the pattern from the text alone.\n\n## How to publish (from this instance)\n\n### Where your credentials are\n\nRead both values from **`/rails/.leonardo/instance.json`**, which your shell can\nread:\n\n```json\n{ \"instance_name\": \"leo-example\", \"mothership_api_token\": \"…\", \"mothership_url\": \"https://llamapress.ai\" }\n```\n\n- `instance_name` — this box's dns name, sent as a normal JSON field.\n- `mothership_api_token` — sent as `Authorization: Bearer \u003ctoken\u003e`.\n\n**Do not use the `MOTHERSHIP_API_TOKEN` environment variable, and do not look\nfor a `.env` file.** Your shell runs inside the Rails container with secrets\nstripped out, so `printenv MOTHERSHIP_API_TOKEN` prints an empty string, and\n`/home/ubuntu/Leonardo/.env` does not exist there. A request built from the\nenvironment variable sends an empty token and the server answers\n`{\"success\":false,\"error\":\"Missing credentials\"}`. The JSON file above is the\nsupported source.\n\n### Step 1 — prove the credentials work\n\nRun this read-only check first. It lists the recipes already published and\nchanges nothing:\n\n```bash\npython3 - \u003c\u003c'PY'\nimport json, urllib.request\nc = json.load(open(\"/rails/.leonardo/instance.json\"))\nreq = urllib.request.Request(\n    \"https://llamapress.ai/api/leonardo/cookbook_recipes?instance_name=\" + c[\"instance_name\"],\n    headers={\"Authorization\": \"Bearer \" + c[\"mothership_api_token\"]})\nprint(urllib.request.urlopen(req).read().decode())\nPY\n```\n\nA `200` with a `recipes` list means you are authenticated. If you get\n`Missing credentials`, you read the token from the wrong place — go back to the\nJSON file.\n\n### Step 2 — publish the recipe\n\nWrite the recipe body to a file first (say `/tmp/recipe.md`), then run:\n\n```bash\npython3 - \u003c\u003c'PY'\nimport json, urllib.request\nc = json.load(open(\"/rails/.leonardo/instance.json\"))\npayload = {\n    \"instance_name\": c[\"instance_name\"],\n    \"slug\": \"glow-toggle\",                    # lowercase letters/digits/dashes\n    \"title\": \"Toggle with a glow effect\",\n    \"summary\": \"Reusable glowing toggle switch.\",\n    \"category\": \"UI Components\",\n    \"body\": open(\"/tmp/recipe.md\").read(),\n}\nreq = urllib.request.Request(\n    \"https://llamapress.ai/api/leonardo/cookbook_recipes\",\n    data=json.dumps(payload).encode(),\n    headers={\"Authorization\": \"Bearer \" + c[\"mothership_api_token\"],\n             \"Content-Type\": \"application/json\"})\nprint(urllib.request.urlopen(req).read().decode())\nPY\n```\n\nThe token never reaches the command line this way, so it cannot leak into a\nshell history or a log line.\n\nThe response returns the public URLs:\n\n```json\n{ \"success\": true, \"created\": true,\n  \"url\": \"https://llamapress.ai/cookbook/u/richie/glow-toggle\",\n  \"md_url\": \"https://llamapress.ai/cookbook/u/richie/glow-toggle.md\",\n  \"index_url\": \"https://llamapress.ai/cookbook/u/richie\" }\n```\n\n**Always report the `url` back to the user** so they can see and share it.\nPOSTing the same `slug` again updates the recipe in place — republishing an\nimproved version is the normal flow. Pass `\"visibility\": \"unlisted\"` to keep a\nrecipe off the index while still reachable by URL. `GET` the same endpoint\n(with the same auth) to list the owner's recipes; `DELETE\n/api/leonardo/cookbook_recipes/\u003cslug\u003e` removes one.\n\n## How to reuse a recipe (on any instance)\n\nFetch the raw Markdown and recreate the pattern in this app's codebase,\nadapting names and styles to the current app:\n\n```bash\ncurl -s https://llamapress.ai/cookbook/u/\u003chandle\u003e/\u003cslug\u003e.md\n```\n\nThe user's index of recipes is at\n`https://llamapress.ai/cookbook/u/\u003chandle\u003e.json` — check it when the user says\n\"use my cookbook\" or \"like the one I saved from my other Leo\".\n\n## Gotchas\n\n- `Missing credentials` means the token, the `instance_name`, or both arrived\n  empty. It is almost always the environment variable: read both values from\n  `/rails/.leonardo/instance.json` instead.\n- A recipe can only be published to the cookbook of the user who OWNS this\n  instance — the server derives the owner from the instance token; there is no\n  way to write to someone else's cookbook.\n- Recipes are **public by default**. Never publish secrets, API keys, customer\n  data, or anything from the app's database — code patterns only. If the code\n  contains a credential, replace it with a placeholder before publishing.\n- Body limit is 200 KB of Markdown. Keep one recipe = one pattern.\n- The slug is permanent once shared: people bookmark the URL, so prefer\n  updating an existing slug over creating near-duplicates.\n"}