---
name: kepano-obsidian-vault
description: Understand and maintain the kepano Obsidian vault template — the three-layer Categories/Templates/Bases system for personal knowledge management.
platforms: [linux, macos, windows]
related_skills: [obsidian]
tags: [obsidian, pkm, bases, templates, categories]
---

# kepano Obsidian Vault — Categories / Templates / Bases System

**Source:** https://github.com/kepano/kepano-obsidian  
**Author:** Steph Ango (kepano) — Obsidian CEO, Minimal theme creator

This vault uses a **three-layer architecture** where every content type is expressed as a coordinated trio: a Template, a Category note, and a Base file. Understanding how these three reinforce each other is the key to using and extending the vault correctly.

---

## The Three Layers

### Layer 1 — Templates (`Templates/*.md`)

Note blueprints applied via Templater. Each template defines:

- A `categories` frontmatter property linking to its Category note
- Domain-specific properties (`rating`, `genre`, `director`, etc.)
- Optional inline base embeds for context-aware related views

**Example — Movie Template:**
```yaml
---
categories:
  - "[[Movies]]"
cover:
genre: []
director:
cast: []
runtime:
rating:
year:
last: {{date}}
imdbId:
via:
---
```

**Example — People Template** (also embeds a base):
```yaml
---
categories:
  - "[[People]]"
birthday:
org: []
created: {{date}}
---
## Meetings

![[Meetings.base#Person]]
```

The `![[Meetings.base#Person]]` embed is **context-aware**: when viewed on a specific person's note, the `Person` view filters meetings where that person appears in the `people` field.

---

### Layer 2 — Category Notes (`Categories/*.md`)

Category notes serve as **MOC (Map of Content) index pages** and as **named link targets** for the `categories` property.

Each category note:
- Has `tags: [categories]` in frontmatter (for legacy/utility filtering)
- Embeds one or more named base views in its body

**Example — Categories/Movies.md:**
```markdown
---
tags:
  - categories
---
## Favorites

![[Movies.base#Favorites]]

## Last seen

![[Movies.base#Last seen]]
```

The category note is both the **navigation hub** for that type and the **anchor** that content notes link to via `[[Movies]]`.

---

### Layer 3 — Base Files (`Templates/Bases/*.base`)

YAML files defining Obsidian Bases queries + named views. The filter always reads the `categories` link property to find relevant notes.

**Example — Templates/Bases/Movies.base (abridged):**
```yaml
filters:
  and:
    - categories.contains(link("Movies"))
    - '!file.name.contains("Template")'
properties:
  file.name:
    displayName: Name
  note.rating:
    displayName: Rating
  note.director:
    displayName: Director
views:
  - type: table
    name: All
    sort:
      - property: director
        direction: ASC
  - type: table
    name: Favorites
    filters:
      and:
        - note.rating > 6
    sort:
      - property: file.name
        direction: ASC
  - type: table
    name: Last seen
    filters:
      and:
        - "!last.isEmpty()"
    sort:
      - property: last
        direction: DESC
    limit: 20
  - type: table
    name: Director
    filters:
      and:
        - list(director).contains(this)   # context-aware!
```

---

## The Data Flow

```
Content Note (e.g. "The Matrix.md")
  frontmatter: categories: ["[[Movies]]"]
         ↓ creates a wiki-link
Category Note ("Categories/Movies.md")
  body embeds: ![[Movies.base#Favorites]]
         ↓ queries via Obsidian Bases
Base File ("Templates/Bases/Movies.base")
  filter: categories.contains(link("Movies"))
  views: All, Favorites, To-watch, Last seen, Director, Actor…
```

The `categories` property is the **single source of truth** binding all three layers together. One link in frontmatter drives discovery across the entire system.

---

## Key Patterns

### 1. `categories` as the Universal Classifier
Every content note carries `categories: ["[[TypeName]]"]`. This:
- Creates a backlink to the Category note (visible in graph view)
- Feeds the base filter `categories.contains(link("TypeName"))`
- Allows a note to belong to multiple categories (it's a list type)

### 2. Context-Aware Views with `this`
Bases support a `this` keyword referring to the **note the base is embedded in**. Use this to create relational views:

| Pattern | Meaning |
|---------|---------|
| `list(director).contains(this)` | Notes where `director` includes the current note |
| `list(people).contains(this)` | Meetings where the current person appears |
| `list(cast).contains(this)` | Movies where the current actor appears |
| `file.links.contains(this.file)` | Notes that link to the current note (e.g. a theater) |

This lets you embed a base in a **Person template** and get all movies by that person automatically.

### 3. Named Views for Embedded Sectioning
A single `.base` file contains multiple named views. Category notes embed specific views:
```
![[Movies.base#Favorites]]
![[Movies.base#Last seen]]
```
You can embed the **same base** in multiple places with different views surfaced.

### 4. Template Embedding of Bases
Templates embed bases directly so every note of that type gets a live relational section:
```markdown
## Meetings
![[Meetings.base#Person]]
```
This appears on every Person note and automatically shows meetings involving that person.

### 5. Excluding Templates from Queries
Always include this filter in every base to prevent template files from appearing in results:
```yaml
- '!file.name.contains("Template")'
```

---

## Folder Structure

```
vault/
├── (root)                 ← Personal notes, journals, evergreen ideas
├── References/            ← External entities: books, movies, people, places
├── Clippings/             ← Things others wrote: articles, essays
├── Attachments/           ← Images, audio, video, PDFs
├── Daily/                 ← Daily notes (YYYY-MM-DD.md)
├── Categories/            ← 21 category/index notes (Movies.md, Books.md, …)
├── Notes/                 ← General notes
└── Templates/
    ├── *.md               ← ~55 note templates
    └── Bases/
        └── *.base         ← 31 Obsidian Bases query files
```

---

## Creating a New Category Type

Follow this checklist when adding a new content type (e.g. "Podcasts"):

### Step 1 — Category Note (`Categories/Podcasts.md`)
```markdown
---
tags:
  - categories
---
## All podcasts

![[Podcasts.base#All]]

## Favorites

![[Podcasts.base#Favorites]]
```

### Step 2 — Template (`Templates/Podcast Template.md`)
```markdown
---
categories:
  - "[[Podcasts]]"
host: []
genre: []
url:
rating:
created: {{date}}
---
## Episodes

![[Podcast episodes.base#Podcast]]
```

### Step 3 — Base File (`Templates/Bases/Podcasts.base`)
```yaml
filters:
  and:
    - categories.contains(link("Podcasts"))
    - '!file.name.contains("Template")'
properties:
  file.name:
    displayName: Name
  note.host:
    displayName: Host
  note.rating:
    displayName: Rating
  note.genre:
    displayName: Genre
views:
  - type: table
    name: All
    order:
      - file.name
      - host
      - rating
      - genre
    sort:
      - property: file.name
        direction: ASC
  - type: table
    name: Favorites
    filters:
      and:
        - note.rating > 6
    sort:
      - property: rating
        direction: DESC
```

**Naming rule:** The string in `link("Podcasts")` must exactly match the Category note filename (`Podcasts.md`) and the base filename (`Podcasts.base`). All three must be **pluralized consistently**.

---

## Property Conventions

| Convention | Example |
|-----------|---------|
| Always pluralize | `categories`, `genres`, not `category`, `genre` |
| Short names | `start` not `start-date` |
| Default list type | `genre: []` (allows multiple values) |
| Reuse across types | `genre` works for Movies, Books, Podcasts |
| Date format | `YYYY-MM-DD` everywhere |
| Rating scale | Integer 1–7 (7 = perfect, 1 = evil) |

---

## Obsidian Bases Syntax Reference

```yaml
# Filter by category link
- categories.contains(link("Movies"))

# Exclude templates
- '!file.name.contains("Template")'

# Filter by tag (use file.tags, NOT tags, in Obsidian 1.9+)
- '!file.tags.contains("daily")'

# Context-aware: items linked to the current note
- list(director).contains(this)
- file.links.contains(this.file)

# Property checks
- note.rating > 6
- "!last.isEmpty()"
- last.isEmpty()

# Formula example
formulas:
  Age: (now() - birthday).years.floor()
```

---

## Maintenance Pitfalls

- **Use `file.tags` not `tags`** in base filters (breaking change in Obsidian 1.9+)
- **Pluralize everything** — `categories` not `category`; mismatch breaks base filters silently
- **Link quotes are required** — `link("Movies")` not `link(Movies)`
- **Template exclusion filter is mandatory** — without it, templates appear in every base view
- **Base file lives in `Templates/Bases/`** but is referenced from `Categories/` — the path in the embed `![[Movies.base#View]]` resolves by filename, not folder
- **`categories` must be a list** even with one value: `- "[[Movies]]"` not `"[[Movies]]"`
- **Composable templates** — a note can set `categories: ["[[People]]", "[[Authors]]"]` to appear in both bases

---

## The Philosophy

> "Having a consistent style collapses hundreds of future decisions into one." — Steph Ango

- **Bottom-up, not top-down**: Create notes first, structure emerges via links and categories
- **Folders are avoided** for organization — categories + bases replace folder hierarchies
- **Links over tags**: `categories` uses wikilinks, not tags; tags are legacy
- **File over app**: Plain markdown + YAML frontmatter — the data survives any tool change
- **Unresolved links are intentional**: `[[Future Note]]` is a breadcrumb, not an error
