Simple Content Management Service
  • Python 97%
  • PowerShell 1.7%
  • Shell 1.3%
Find a file
Erikson Arias 69b2bfafa6
All checks were successful
Deploy Pylon image / push-image (push) Successful in 16s
fix: post-hooks take too long to run
2026-06-22 17:20:01 -04:00
.forgejo/workflows fix: build command 2026-06-13 08:45:25 -04:00
scms fix: post-hooks take too long to run 2026-06-22 17:20:01 -04:00
.gitignore feat: allow scms to be installed 2026-06-14 22:34:08 -04:00
.gitkeep feat: transition to pylon-first functionality 2026-06-14 00:02:44 -04:00
app.py feat: allow scms to be installed 2026-06-14 22:34:08 -04:00
config-example.yml feat: transition to pylon-first functionality 2026-06-14 00:02:44 -04:00
docker-compose.pylon.yml feat: add python package thin client 2026-06-14 11:49:43 -04:00
Dockerfile.pylon feat: create pylon image 2026-06-13 00:31:33 -04:00
pyproject.toml feat: allow scms to be installed 2026-06-14 22:34:08 -04:00
README.md feat: add --all flag to pull, add update command 2026-06-14 23:09:10 -04:00
requirements.txt feat: add pylon mode 2026-06-12 22:34:29 -04:00
setup.ps1 feat: allow scms to be installed 2026-06-14 22:34:08 -04:00
setup.sh feat: allow scms to be installed 2026-06-14 22:34:08 -04:00

SCMS

Simple Content Management Service is a CLI tool for publishing bundles of Markdown files and static media, called content packages, to a remote Pylon server. Webservers are intended to consume the index JSON and Markdown files from the Pylon for rendering.

Quickstart

scms publish <prefix>            # Publish a content package
scms publish <prefix> --force    # Force publish (even if no changes detected)
scms publish <prefix> --shadow   # Shadow publish a content package
scms unpublish <prefix>          # Unpublish a content package
scms pull <prefix>               # Pull a content package from a Pylon
scms pull --all                  # Pull every package from the Pylon
scms packages                    # List all published packages on a Pylon

scms pylon start                 # Run SCMS in Pylon Mode
scms pylon index                 # Rebuild the index on all configured Pylons
scms pylon show                  # List configured Pylon connectors
scms pylon show "<pylon-name>"   # Show remote Pylon configuration

Installation

Local setup

Linux/macOS:

curl -fsSL https://git.eriksonarias.dev/eriksonarias/scms/raw/branch/main/setup.sh | bash

Windows (PowerShell):

irm https://git.eriksonarias.dev/eriksonarias/scms/raw/branch/main/setup.ps1 | iex

The scripts install SCMS via pipx (no virtual environment required), create a content/ directory, and run the interactive config wizard.

Upadting

After installation, SCMS can be updated with the update command.

scms update

Development

Create a virtual environment and install requirements:

# Create a virtual environment
python -m venv venv

# Activate virtual environment
source venv/bin/activate      # Linux/macOS
.\venv\Scripts\Activate.ps1   # Windows

# Install requirements
python -m pip install -r requirements.txt

Configure SCMS by creating a config.yml file (see config-example.yml for a complete example).

Configuration

Client configuration

Each profile on the client side configures one or more Pylon connectors, which distributes content to its own connectors server-side.

profiles:
  default:
    connectors:
      - name: my pylon
        url: https://host:port
        token: abcdefg
        profile: default     # Optional, Pylon-side profile to use
    ignore:
      - content/wip

Profiles

Profiles let you switch between environments (e.g. development vs production) at the command line. A profile called default is required.

All commands can take a --profile argument.

scms publish <prefix> --profile <profile-name>
scms publish <prefix> -p <profile-name>

Pylon server configuration

The pylon top-level key configures Pylon Mode (the server). The server's own connectors and post-hooks are configured under pylon.profiles:

pylon:
  token: abcdefg          # Required. Generate with: openssl rand -hex 32
  host: 0.0.0.0           # Optional. Default: 0.0.0.0
  port: 8000              # Optional. Default: 8000
  store_files: true       # Optional. Store uploaded files on the Pylon server. Default: false
  profile: default        # Optional. Default server-side profile to use
  index_directory: index  # Optional. Where to store index files. Default: index

  profiles:
    default:
      connectors:
        - name: minio
          type: s3
          host: example.com:9000
          access_key: abcd
          secret_key: efgh
          bucket: content
          destination: content  # Optional prefix within bucket
      post_hooks:
        - name: Ping website
          type: rest
          url: http://example.com/content-load
      ignore:
        - content/wip

Ignores

Directories can be excluded from indexing and publishing. Useful for work-in-progress content:

profiles:
  default:
    ignore:
      - content/wip
      - content/drafts

Content packages

Content packages (or simply packages) are directories of files that SCMS manages as a single unit. A package is identified by the presence of a .scms.yml file in its directory.

content/
├─ posts/
│  ├─ my-first-post/
│  │  ├─ my-first-post.md
│  │  └─ .scms.yml
│  └─ another-post/
│     ├─ another-post.md
│     └─ .scms.yml
└─ about-me/
   ├─ about-me.md
   └─ .scms.yml

The three packages above have IDs:

  • posts/my-first-post
  • posts/another-post
  • about-me

Creating a package

Create a subdirectory under content/ with a .scms.yml file (may be empty). Each package should have at most one Markdown file named identically to its directory (e.g. my-article/my-article.md).

Publishing

scms publish <prefix>
scms publish <prefix> --force # Force publish, even if no files have changed
scms publish <prefix> -f

Publishes all packages matching the given prefix to all configured Pylons. Only new or modified files are uploaded by default; deleted files are propagated to the Pylon automatically.

Prefixes

A prefix selects one or more packages. Use / or an empty string to publish everything:

scms publish /                    # All packages
scms publish posts/               # All packages under posts/
scms publish posts/my-first-post  # One specific package

Shadow publishing

scms publish <prefix> --shadow
scms publish <prefix> -s

Pushes content with a shadow flag. Shadow-published content is included in the shadow index but excluded from the public index. Whether shadow content is rendered is at the discretion of the consuming webserver.

Bypassing prompts

Some commands such as publish will affect multiple packages and display a prompt to continue execution.

Use -Y to skip confirmation prompts (useful for scripting):

scms publish / -Y
scms unpublish <prefix> -Y
scms pull <prefix> -Y

Unpublishing

scms unpublish <prefix>

Removes all files for matching packages from the Pylon (and its connectors) and removes them from the index. The local content directory is not affected; republishing later will treat the content as new.

Pulling

scms pull <prefix>

Downloads a content package from the Pylon back into the local content/ directory. Useful for syncing content to a new machine or recovering files. If files already exist locally, a confirmation prompt is shown (bypass with -Y).

scms pull --all
scms pull -a

All content packages can be pulled using the --all flag.

Listing packages

scms packages
scms packages --shadow     # Include shadow-published packages
scms packages -p <profile>

Lists all packages on the configured Pylon, showing package ID, status (published/shadow), file count, publish date, and connectors.

Index

scms index
scms index --force-rebuild

Builds or updates the SQLite index database and JSON index files for all Pylons:

  • {profile}-index.json for published, non-shadow packages only
  • {profile}-shadow-index.json for all published packages including shadow

Pylon

Pylon is the SCMS server component. All publishing is routed through a Pylon; the client never writes directly to connectors (S3, FTP, etc.).

When configured, a Pylon:

  • Receives uploaded packages from the client.
  • Distributes files to its own configured connectors.
  • Maintains a SQLite index and exports JSON index files.
  • Runs post-hooks after every publish or unpublish.

Running Pylon Mode

scms pylon start
scms pylon start --host 0.0.0.0 --port 8000

Or with Docker:

docker compose -f docker-compose.pylon.yml up -d

Pylon endpoints

Method Path Description
GET /health Health check (unauthenticated)
POST /publish Upload and publish a content package
POST /unpublish Remove a content package and update the index
GET /pull Download a package as a zip
GET /index Return the JSON index
GET /packages List all packages with status and connector info
GET /config Return the active server configuration (redacted)
PUT /content Upload a single file
DELETE /content Delete a file or directory
POST /hooks Run post-hooks

Inspecting a Pylon

scms pylon show                   # List all configured Pylon connectors
scms pylon show "<pylon-name>"    # Show Pylon server-side config
scms pylon show "<pylon-name>" -p <profile>