Build a Markdown-powered blog with vault publishing, custom domains, and local sync.
In this guide you will set up a fully functional Markdown blog hosted on Lifestream Vault.
By the end you will have:
blog.example.com) pointing to your vaultEverything is managed through the Lifestream Vault UI, the SDK, or the lsvault CLI — use whichever fits your workflow.
Prerequisites
A vault is an isolated container for documents. Each vault gets its own URL namespace and can be published independently, making it the right unit for a blog.
Via the UI: open Settings → Vaults → New Vault, enter a name (e.g. My Blog) and a URL-friendly slug (e.g. my-blog), then click Create.
Alternatively, use the SDK or CLI below.
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
const { client } = await LifestreamVaultClient.login(
'https://vault.lifestreamdynamics.com',
'you@example.com',
'your-password',
);
// The slug is generated from the name — you do not pass it.
const vault = await client.vaults.create({
name: 'My Blog',
});
console.log('Vault created:', vault.id, '— slug:', vault.slug);Your profile slug becomes the first segment of every published URL. Set it once and it applies to all vaults you publish.
jane) — lowercase letters, numbers, and hyphens onlyYour slug is also used on your public author page, which lists all published vaults.
Once publishing is enabled, your blog will be available at:
https://vault.lifestreamdynamics.com/{profileSlug}/{vaultSlug}
For example: https://vault.lifestreamdynamics.com/jane/my-blog
Blog posts are standard Markdown files stored inside your vault. A posts/ folder keeps things organised, but any path works.
Lifestream Vault reads YAML frontmatter at the top of each file to populate metadata. The supported fields are:
| Field | Purpose |
|---|---|
title | Post title (overrides the first H1) |
date | Publication date (ISO 8601, e.g. 2026-03-01) |
tags | Array of category tags |
description | Short summary shown in listings and SEO |
A minimal post looks like this:
---
title: Hello, World
date: 2026-03-01
tags: [intro, meta]
description: My first post on Lifestream Vault.
---
# Hello, World
Welcome to my new blog powered by Lifestream Vault!
const content = `---
title: Hello, World
date: 2026-03-01
tags: [intro, meta]
description: My first post on Lifestream Vault.
---
# Hello, World
Welcome to my new blog powered by Lifestream Vault!
`;
const doc = await client.documents.put(
vault.id,
'posts/hello-world.md',
content,
);
console.log('Document created:', doc.id);Vault publishing makes the entire vault publicly browsable — visitors can navigate your posts, use the built-in search, and view your profile page.
Enabling vault publishing does not publish individual documents automatically; it makes the vault visible as a site. You control which posts appear in the listing by publishing them individually (see the next section).
Via the UI: open your vault, go to Settings → Publishing, and toggle Enable vault publishing.
When publishing your vault, you can customize the URL slug, add a site title and description, and configure a custom favicon. These options are available in Settings → Publish.
// publishVault.publish is positional: (vaultId, params). slug and title
// are required; the rest are optional site-configuration fields.
await client.publishVault.publish(vault.id, {
slug: 'my-blog',
title: 'My Blog',
});
console.log('Vault is now public!');After enabling, your vault index is immediately accessible at:
https://vault.lifestreamdynamics.com/{profileSlug}/{vaultSlug}
No build step or deployment required.
Publishing a document gives it a canonical public URL and exposes optional SEO fields:
| Field | Purpose |
|---|---|
slug | URL-friendly identifier (e.g. hello-world) |
seoTitle | <title> tag override |
seoDescription | <meta name="description"> value |
ogImage | Open Graph image URL for social sharing |
A published post becomes accessible at:
https://vault.lifestreamdynamics.com/{profileSlug}/{docSlug}
Via the UI: open the document, click the Publish button in the toolbar, fill in the SEO fields, and click Publish Now.
// publish.create is positional: (vaultId, documentPath, params)
const published = await client.publish.create(
vault.id,
'posts/hello-world.md',
{
slug: 'hello-world',
seoTitle: 'Hello, World — My Blog',
seoDescription: 'My first post on Lifestream Vault.',
ogImage: 'https://cdn.example.com/hello-world-og.png',
},
);
// The public URL is built from your profile slug and the post slug:
// https://vault.lifestreamdynamics.com/{profileSlug}/{published.slug}
console.log('Published with slug:', published.slug);Custom domains require a Business plan. Upgrade in Settings → Subscription.
Point blog.example.com (or any subdomain) to your vault with two DNS records:
Step 1 — TXT record for ownership verification:
| Type | Host | Value |
|---|---|---|
TXT | _lsv-verify.blog.example.com | lsv-verify=<token> |
The verification token is returned when you create the domain (see below).
Step 2 — CNAME to route traffic:
| Type | Host | Value |
|---|---|---|
CNAME | blog.example.com | vaults.lifestreamdynamics.com |
DNS propagation typically takes 1–60 minutes. Once the TXT record resolves, verification runs automatically via the background domain-verification worker — no manual trigger is needed. Monitor domain status in Settings → Custom Domains or via the SDK/CLI.
// Step 1 — register the domain (returns verification token)
const domainRecord = await client.customDomains.create({
domain: 'blog.example.com',
});
console.log('Add this TXT record:', domainRecord.verificationToken);
// Verification happens automatically via the background domain-verification worker.
// Poll the domain status to check progress — get(domainId) is positional:
const status = await client.customDomains.get(domainRecord.id);
console.log('Domain status:', status.status); // 'pending' → 'verified'The lsvault sync commands let you write posts in any local editor (VS Code, Obsidian, Neovim, etc.) and push changes to your vault automatically.
Workflow options:
| Mode | Best for |
|---|---|
| push | One-time bulk upload of a directory |
| watch | Interactive session — pushes on file save |
| daemon | Background service — always-on sync |
First, initialise sync for the vault by pointing it at a local directory:
# Link your local ./blog directory to the vault.
# init prints a sync config ID — use it with push/watch below.
lsvault sync init <VAULT_ID> ./blog --mode push# <SYNC_ID> is the config ID printed by 'sync init'
lsvault sync push <SYNC_ID>The daemon runs in the background using a PID file and restarts automatically on reboot when installed as a system service. Stop it with lsvault sync daemon stop.
Keep posts under a posts/ directory and static assets (images, attachments) under assets/. A clean layout makes bulk operations easier:
my-blog/
├── posts/
│ ├── 2026-03-01-hello-world.md
│ └── 2026-03-15-second-post.md
└── assets/
└── hello-world-og.png
title, date, and description — they drive listing pages and SEOYYYY-MM-DD) so sorting works correctlyTags are first-class citizens in Lifestream Vault. Visitors can filter the vault listing by tag, so use consistent, lowercase tags (e.g. typescript, tutorial, release-notes).
In Settings → Vault → Search, enable full-text indexing so visitors can search your posts instantly via the built-in search bar.
If you run a static site generator alongside your vault, configure a webhook (Settings → Webhooks → New) on document.created and document.updated events to trigger an external build pipeline automatically.
Lifestream Vault does not host binary assets. For images referenced in posts, use a CDN (Cloudflare R2, Cloudinary, or a public GitHub repo with raw.githubusercontent.com URLs).
You now have a live Markdown blog backed by Lifestream Vault. Here are some places to go next:
client.publish, client.publishVault, and client.customDomainslsvault publish-vault and lsvault publish flags.lsvaultignore patterns, and bidirectional sync