Alles, was es kann, auf einer Seite.

Geprüft im Code, der es ausführt, am 2. September 2026. Nichts hier ist geplant oder kommt demnächst.

Der Admin gibt es auf Englisch und Italienisch, deshalb sind diese Aufnahmen der englische Satz.

Das Schema ist das Formular.

Was du links schreibst, bekommen die Redakteure rechts.

  • fields.text()
  • fields.textarea()
  • fields.richText()
  • fields.number()
  • fields.boolean()
  • fields.datetime()
  • fields.slug()
  • fields.select()
  • fields.relation()
  • fields.media()
  • fields.group()
  • fields.array()
  • fields.json()
  • fields.blocks()
Flags, keine Bildschirme
required und localized sind zwei Booleans am Feld.
Slug aus dem Titel
fields.slug({ from: 'title' }) füllt sich selbst; select trägt feste Optionen.
Auch aus dem Admin
Eigene Collections lassen sich im Admin anlegen und in der Datenbank speichern.

Ein Inhalt, vier Wege ihn zu lesen.

Nimm den, den dein Stack schon spricht.

Öffentliche Einträge brauchen keinen Schlüssel. Back-Office-Dokumente brauchen einen API-Schlüssel oder ein Bearer-Token.

shellHTTP
curl 'https://cms.altovar.net/api/v1/collections/blog-post/entries?locale=en'

{ "data": [ { "id": "fdoc_16d9i3ef", "locale": "en",
              "data": { "title": "EU-first content infrastructure", … },
              "seo":  { "metaTitle": …, "metaDescription": …, "robots": "index,follow" } } ],
  "pagination": { "page": 1, "limit": 20, "total": 3, "totalPages": 1 } }

# The back-office route is the same shape, with a key:
curl -H 'x-api-key: sk_…' \
     'https://cms.altovar.net/api/v1/collections/blog-post/documents?limit=10'

Ein typisierter Client mit derselben Hülle: data und pagination.

blog.tsTS
import { OspreyClient } from '@osprey/client'

const osprey = new OspreyClient({
  baseUrl:  'https://cms.altovar.net',
  tenantId: 'altovar',
})

const { data, pagination } = await osprey
  .collection('blog-post')
  .listEntries({ locale: 'en', limit: 10 })

Nur Queries, per POST. GraphiQL steht in der Entwicklung bereit.

graphqlGQL
POST /api/v1/graphql
x-api-key: sk_…

query Posts($limit: Int) {
  documents(collectionSlug: "blog-post", status: "published", limit: $limit) {
    data { id status data }
    pagination { total }
  }
}

Einträge landen beim Build in Astro Content Collections. Kein Schlüssel am Edge.

src/pages/index.astroASTRO
---
import { getCollection } from 'astro:content'

const posts = (await getCollection('blog'))
  .filter((p) => p.data.locale === 'en')
  .sort((a, b) => b.data.publishedAt.localeCompare(a.data.publishedAt))
---
{posts.map((p) => (
  <article>
    <a href={'/' + p.data.slug}>{p.data.title}</a>
    <p>{p.data.excerpt}</p>
  </article>
))}
Das OpenAPI-Dokument lesen →

Hier veröffentlichen, dort erscheinen.

Entwürfe bleiben privat. Ein veröffentlichter Eintrag erreicht den öffentlichen Endpunkt innerhalb einer Minute, und ein Webhook kann die Website neu bauen.

Drei Status
draft, published, archived. Seiten ergänzen Review, Freigabe und Zeitplanung.
Signierter Webhook
HMAC-SHA256 über den Body in x-osprey-signature. Neun Ereignisse, Secret verschlüsselt gespeichert.
Cache-Header
Öffentliche Lesezugriffe tragen max-age=60 mit stale-while-revalidate, bereit für jeden vorgeschalteten Cache.

Zehn Sprachen, eine Matrix.

Jedes lokalisierte Feld, jede Sprache, gezählt.

Inhaltssprachen
en, it, es, de, fr, nl, pl, sv, pt, ro, pro Workspace gesetzt.
Admin
Englisch und Italienisch, umschaltbar in der oberen Leiste.
Abdeckung
Eine Matrix pro Seite und Sprache, und die fehlenden Texte als CSV.
Auslieferung
Ein Query-Parameter wählt die Sprache; hreflang-Alternates reisen mit jedem Eintrag.

Deine Domain, in vier Schritten.

Heute über die API. Die Einstellungsseite ist unterwegs, und diese Seite sagt das auch.

  1. Beanspruchen Sende den Host per POST an den Domain-Endpunkt. Er wartet als ausstehend.
  2. Nachweisen Lege den TXT-Eintrag _osprey-verify.<host> mit dem erhaltenen Token an.
  3. Prüfen Sende erneut POST mit verify: true. Der Host wandert in den Workspace.
  4. Zeigen CNAME auf die Plattform. Das Zertifikat wird bei der ersten Anfrage ausgestellt.
domainsHTTP
POST /api/v1/admin/domains
Authorization: Bearer <jwt>
{ "host": "www.example.com" }

{ "status": "pending",
  "verification": { "type": "TXT",
                    "name": "_osprey-verify.www.example.com",
                    "value": "osprey-verify=…" } }

Eigene Domains hängen vom Plan oder vom Hosting-Add-on ab.

Kontrolle, die ihre Arbeit zeigt.

Jede Zeile unten ist eine Route und ein Bildschirm, keine Roadmap.

Rollen
admin, editor, author, contributor, viewer
API-Schlüssel
gehasht, einmal gezeigt, Nur-Lesen-Flag, Ablauf
Audit-Log
wer, was, wann und von wo, für jede Statusänderung
Revisionen
Seitenhistorie mit Diff und Wiederherstellung
Papierkorb
gelöschte Seiten warten; du stellst sie wieder her oder löschst sie endgültig
Gemeinsam
Präsenz und geteiltes Bearbeiten von Seiten, Feld für Feld
Einwilligung
Cookie-Banner und ein Einwilligungsprotokoll mit gehashter IP
Löschung und Export
ein Aufruf schwärzt eine betroffene Person; Export aus dem Admin oder der CLI
Anmeldung
OpenID Connect pro Workspace, oder ein lokales Passwort
Rate-Limits
ein Bucket pro Routenfamilie, Grenzen in den Antwort-Headern

Die Teile drumherum.

Zwei veröffentlichte Pakete, eine CLI, ein MCP-Server und ein Self-Host-Stack.

@osprey/client
typisiertes SDK, React-Block-Renderer, Consent-Manager
@osprey/astro
Content-Loader, SEO-Head, Übersetzungs-Sync
CLI
setup, init, dev, build, generate-types, link, export
MCP-Server
sechzehn Werkzeuge über stdio, für den KI-Agenten, den du schon nutzt
Self-Host
eine Compose-Datei und ein Einrichtungsassistent unter /setup
KI-Assistent
übersetzt Lücken, füllt SEO, schreibt Alt-Texte; aus, bis du ihn einschaltest

Dein Inhalt. Dein Server. Heute.

Kostenloser Plan, keine Karte. Gehostet in der EU, oder auf deinen eigenen Maschinen.