Collections & CMS @ stwoo.net

In Gohyde, posts and pages are just two built-in collections. Any number of
custom collections (_projects, _team, _docs, …) can be defined in
_config.yml or created from the CMS.

Defining collections

collections:
  - name: projects
    label: Projects
    output: true        # write items to the site (default true)
    layout: project     # default layout for items
    src: _projects      # source dir (default _<name>)
    blueprint: showcase # optional shared field schema (see Blueprints)

Collection content lives in the src dir as Markdown/HTML files with front
matter:

---
layout: project
title: Routeway Logistics
weight: 1
featured: true
---

Asset-based logistics across North America.

Items are available in templates as site.projects:

{% for project in site.projects %}
  <article>
    <h2>{{ project.title }}</h2>
    {{ project.content }}
  </article>
{% endfor %}

output: false collections are loaded and queryable but not written to the
output directory.

Blueprints (shared field schemas)

A blueprint is a named, reusable list of field definitions stored under a
top-level blueprints: key. Assign it to multiple collections so they share one
schema:

blueprints:
  showcase:
    label: Showcase
    fields:
      - { name: title,    label: Title,    type: text,   required: true }
      - { name: summary,  label: Summary,  type: textarea }
      - { name: weight,   label: Weight,   type: number }
      - { name: featured, label: Featured, type: boolean }

collections:
  - { name: projects, label: Projects, blueprint: showcase, layout: project }
  - { name: clients,  label: Clients,  blueprint: showcase, layout: client }

When a collection has a blueprint, the CMS edits its items against the
blueprint's fields; otherwise it uses the collection's inline fields:.

The CMS

gohyde serve mounts a CMS at /_gohyde (disable with --no-cms). It edits
content files and _config.yml directly — no database.

What you can do:

The API is same-origin only — no CORS is granted, so web pages from other
origins can't reach the CMS through your browser. File-path parameters are
validated against directory traversal. Config writes keep a .bak backup.

Users & permissions

The CMS is open (no login) until you create a user:

gohyde user add alice --role admin
gohyde user add bob --role editor --collections posts,projects
gohyde user list                     # gohyde user remove NAME to revoke

Two roles:

Role Can do
admin everything — content, media, config, collections, blueprints, data files
editor content in granted collections + media browse/upload; no site structure or config

An editor with --collections posts sees only Posts in the sidebar; API
requests for anything else return 403. Users are stored in .gohyde/users.yml
with PBKDF2-hashed passwords (mode 0600, excluded from builds). Sessions are
HttpOnly, SameSite cookies that live 24 hours.

Because everything is flat files, version control doubles as an audit log — a
cron that commits the repo periodically captures every CMS edit with history,
and production can build from that repo.

API surface (used by the CMS UI)

GET    /_gohyde/api/collections
POST   /_gohyde/api/collections                 {name, label, is_post, output, blueprint, layout}
PUT    /_gohyde/api/collections/<name>          {label, output, blueprint, layout}
DELETE /_gohyde/api/collections/<name>[?purge=1]
GET    /_gohyde/api/collections/<name>/fields   ← blueprint-aware
GET/PUT/DELETE /_gohyde/api/collections/<name>/items[/<file>]

GET    /_gohyde/api/blueprints
GET/PUT/DELETE /_gohyde/api/blueprints/<name>   {name, label, fields}

_config.yml writes go through a read-doc / edit / write-doc helper that keeps a
.bak backup and preserves the rest of the file.

Field types

type Editor input
text single-line
textarea multi-line
number numeric
boolean checkbox
date date picker
select dropdown (with options:)

Built-in fields include slug — it feeds the :slug token in the
collection's permalink pattern, so editing it changes the item's URL without
renaming the file.

categories/tags are normalized to arrays on save, but the editor accepts
either a list or a space-separated string on load (Jekyll-compatible).