Tutto quello che fa, in una pagina.

Verificato nel codice che lo esegue, il 2 settembre 2026. Niente qui è pianificato o in arrivo.

Lo schema è il form.

Quello che scrivi a sinistra è quello che gli editor trovano a destra.

  • 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()
Flag, non schermate
required e localized sono due booleani sul campo.
Slug dal titolo
fields.slug({ from: 'title' }) si compila da solo; select porta opzioni fisse.
Anche dall'admin
Le collection personalizzate si creano dall'admin e vivono nel database.

Un contenuto, quattro modi di leggerlo.

Scegli quello che il tuo stack già parla.

Le voci pubbliche non chiedono chiave. I documenti di back-office vogliono una chiave API o un 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'

Un client tipizzato con la stessa busta: data e 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 })

Solo query, via POST. GraphiQL è disponibile in sviluppo.

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 }
  }
}

Le voci finiscono nelle content collection di Astro al build. Nessuna chiave sull'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>
))}
Leggi il documento OpenAPI →

Pubblichi qui, compare là.

Le bozze restano private. Una voce pubblicata arriva all'endpoint pubblico entro un minuto, e un webhook può ricostruire il sito.

Tre stati
draft, published, archived. Le pagine aggiungono revisione, approvazione e programmazione.
Webhook firmato
HMAC-SHA256 sul corpo in x-osprey-signature. Nove eventi, segreto cifrato a riposo.
Header di cache
Le letture pubbliche portano max-age=60 con stale-while-revalidate, pronte per qualsiasi cache davanti.

Dieci lingue, una matrice.

Ogni campo localizzato, ogni lingua, contati.

Lingue di contenuto
en, it, es, de, fr, nl, pl, sv, pt, ro, impostate per workspace.
Admin
Inglese e italiano, si cambiano dalla barra in alto.
Copertura
Una matrice per pagina e lingua, e le stringhe mancanti in un CSV.
Distribuzione
Un parametro di query sceglie la lingua; gli alternate hreflang viaggiano con ogni voce.

Il tuo dominio, in quattro passi.

Oggi via API. La pagina nelle impostazioni è in arrivo, e questa pagina lo dice.

  1. Rivendica Fai POST dell'host all'endpoint dei domini. Resta in attesa.
  2. Dimostra Aggiungi il record TXT _osprey-verify.<host> con il token ricevuto.
  3. Verifica Fai di nuovo POST con verify: true. L'host passa al workspace.
  4. Punta CNAME verso la piattaforma. Il certificato viene emesso alla prima richiesta.
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=…" } }

I domini personalizzati dipendono dal piano o dall'add-on di hosting.

Un controllo che mostra il lavoro.

Ogni riga qui sotto è una rotta e una schermata, non una roadmap.

Ruoli
admin, editor, author, contributor, viewer
Chiavi API
hash, mostrate una volta, flag di sola lettura, scadenza
Registro audit
chi, cosa, quando e da dove, per ogni cambio di stato
Revisioni
storia delle pagine con diff e ripristino
Cestino
le pagine eliminate aspettano; le ripristini o le cancelli per sempre
Insieme
presenza e modifica condivisa delle pagine, campo per campo
Consenso
banner cookie e registro dei consensi con IP in hash
Cancellazione ed export
una chiamata oscura un interessato; export dall'admin o dalla CLI
Accesso
OpenID Connect per workspace, oppure password locale
Limiti di traffico
un bucket per famiglia di rotte, limiti negli header di risposta

I pezzi intorno.

Due package pubblicati, una CLI, un server MCP e uno stack self-host.

@osprey/client
SDK tipizzato, renderer di blocchi React, gestore del consenso
@osprey/astro
loader di contenuti, head SEO, sync delle traduzioni
CLI
setup, init, dev, build, generate-types, link, export
Server MCP
sedici strumenti via stdio, per l'agente AI che usi già
Self-host
un file compose e una procedura guidata al primo avvio su /setup
Assistente AI
traduce i buchi, compila la SEO, scrive gli alt; spento finché non lo accendi

Il tuo contenuto. Il tuo server. Oggi.

Piano gratuito, senza carta. Ospitato nell'UE, o sulle tue macchine.