CDNSite
Deployed and supported. The fields and commands described here are stable within version v1alpha1.
apiVersion | cdn.stackngo.io/v1alpha1 |
kind | CDNSite |
| Applied by | sng apply (manifest) or sng cdn create, enable, disable |
| Related commands | sng cdn list · upload · clear-cache · delete, sng kind get CDNSite, sng template gen CDNSite |
What it is for
A CDNSite publishes a static site under a name in your zone, behind an HTTP cache. By
default, the files are stored by the platform (default storage) and you send them with
sng cdn upload. A site can also serve an object storage bucket that you own (spec.s3).
The site can be put into maintenance (503) without losing its content, and a fallback file
(fallback) serves the routes of a single-page application.
Minimal example
apiVersion: cdn.stackngo.io/v1alpha1
kind: CDNSite
metadata:
name: docs
spec:
domain: docs.acme.dev.stackngo.app
sng apply docs-site.yml
sng cdn upload docs ./dist
Equivalent without a manifest: sng cdn create docs (which adds fallback: index.html).
Complete annotated example
apiVersion: cdn.stackngo.io/v1alpha1
kind: CDNSite
metadata:
name: docs # site name: lowercase, digits, '-'; at most 63 characters
spec:
domain: v2.docs.acme.dev.stackngo.app # under <tenant>.dev.stackngo.app, at any depth; no '*'
path: / # served prefix (default "/")
pathType: Prefix # Prefix (default), Exact, or ImplementationSpecific
fallback: index.html # served for an extensionless route that is not found; relative path
maintenanceMode: false # true: 503 on the whole site, content kept
tlsSecretRef: # optional: your certificate (tls.crt and tls.key keys)
name: docs-tls
With your own object storage bucket:
apiVersion: cdn.stackngo.io/v1alpha1
kind: CDNSite
metadata:
name: assets
spec:
domain: assets.acme.dev.stackngo.app
s3:
bucket: acme-assets # never the platform's bucket
endpoint: https://s3.gra.io.cloud.ovh.net # https and public required; empty = AWS
region: gra # default us-east-1
prefix: site/ # bucket folder served at the site root
forcePathStyle: true # path-style URLs (MinIO, some providers)
credentialsSecretRef:
name: acme-assets-s3 # Secret in your tenant, label cdn.stackngo.io/s3-credentials: "true"
accessKeyIDKey: accessKeyId # default accessKeyId
secretAccessKeyKey: secretAccessKey # default secretAccessKey
# sessionTokenKey: sessionToken # temporary token, optional
credentialsSecretRef and tlsSecretRef refer to a secret of your tenant, declared by an
SSMSecret with materialize: true: that is what makes it usable
by a static site. Then set its keys with sng secret set --data:
apiVersion: ssm.stackngo.io/v1alpha1
kind: SSMSecret
metadata:
name: docs-tls
spec:
length: 0 # you set the value yourself
materialize: true # usable by the static site
sng apply docs-tls.yml
sng secret set docs-tls --data "tls.crt=$(cat fullchain.pem)" --data "tls.key=$(cat privkey.pem)"
For credentialsSecretRef, the same with the keys accessKeyId and secretAccessKey (or
those you name in accessKeyIDKey / secretAccessKeyKey). A new value is taken into account
by the site within 15 minutes.
The Secret holding your bucket keys
The keys of credentialsSecretRef are only read from a Secret that you explicitly entrust to
static sites. This Secret must:
- carry the label
cdn.stackngo.io/s3-credentials: "true"; - be of type
Opaque(the default type of a generic Secret); - not be a Secret managed by the platform (registry credential, OIDC credentials of an application, secret declared by another manifest…).
A Secret that does not meet these conditions is refused without any request being sent to
your bucket: the site stays Pending, with the condition S3Reachable=False and the reason
SecretNotAllowed. It is re-evaluated every minute: fix the Secret, and the site switches to
Ready with no other action.
Fields
Cross-field validation rules
The schema has no cross-field rule; it rejects an empty domain or one containing *, a
pathType outside the list, and an s3 without bucket or credentialsSecretRef.name.
The API also rejects, as early as sng apply:
- a
domainin uppercase, with a trailing dot, or that is not a full name (at least two labels); - a
domainunder the platform domain but outside your zone (spec.domain "…" : réservé à la plateforme ou à un autre client). A name under your zone, or under a brought domain, is accepted: see Brought domain below; - a
paththat does not start with/; - an absolute
fallbackors3.prefix, one with.., or one with a character outsideA-Za-z0-9._~@+=,-; - a malformed
s3.bucket, or one equal to the platform's bucket (spec.s3 ne peut pas viser le bucket par défaut : retirer spec.s3); - an
s3.endpointoverhttp, with credentials in the URL, internal to the platform, or resolving to a non-public address; - the same site name or the same domain as another
CDNSitein your tenant, across all contexts (409); - a
metadata.namedifferent from the file name, a tenant specified in the manifest, an unknown field (includingstatus).
CDNSite
CDNSite publie un site statique sur votre domaine, servi par le CDN de la plateforme.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
apiVersion string | cdn.stackngo.io/v1alpha1 | ||
kind string | CDNSite | ||
metadata ObjectMeta | Seul metadata.name est à renseigner (nom de la ressource dans votre tenant). | ||
spec CDNSiteSpec | |||
status CDNSiteStatus |
CDNSiteSpec
CDNSiteSpec décrit le site que vous voulez publier.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
domain string | Domain est le nom d'hôte sous lequel le site est servi (par exemple www.example.com). Le joker (*) est refusé. | MinLength: 1 Pattern: ^[^*]+$ Required: {} | |
path string | Path est le chemin d'URL sous lequel le site est servi (défaut : /). | / | Optional: {} |
pathType string | PathType indique comment le chemin est comparé aux URL demandées : Prefix (le chemin et tout ce qui est en dessous), Exact (ce chemin seul) ou ImplementationSpecific (règle par défaut de la plateforme). | Prefix | Enum: [Prefix Exact ImplementationSpecific] Optional: {} |
maintenanceMode boolean | MaintenanceMode : le site répond 503 à toutes les requêtes ; le domaine et le certificat restent en place. | false | Optional: {} |
fallback string | Fallback est le fichier (chemin relatif à la racine du site) servi quand une route d'application (URL sans extension) ne correspond à aucun fichier. Vide : aucune page de repli. Exemple : "index.html" ou "public/index.html". | Optional: {} | |
s3 CDNSiteS3Spec | Votre propre stockage objet, d'où le site est servi avec vos clés (credentialsSecretRef obligatoire). Absent : le site est servi depuis le stockage fourni par la plateforme. | Optional: {} | |
tlsSecretRef LocalObjectReference | TLSSecretRef désigne le secret de votre tenant qui contient votre propre certificat TLS (clés tls.crt et tls.key). Absent : la plateforme obtient et renouvelle un certificat pour le domaine. | Optional: {} |
CDNSiteStatus
CDNSiteStatus est l'état du site constaté par la plateforme, dont l'emplacement effectivement servi.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
observedGeneration integer | ObservedGeneration est la dernière version du manifeste prise en compte par la plateforme. | Optional: {} | |
phase string | Phase : Pending, Ready, Conflict ou Invalid. | Optional: {} | |
message string | Message explique la phase (raison d'un Invalid, d'un Conflict…). | Optional: {} | |
storage string | Storage : default (stockage fourni par la plateforme) ou custom (votre stockage, spec.s3). | Optional: {} | |
bucket string | Bucket est le bucket d'où le site est servi. | Optional: {} | |
endpoint string | Endpoint est l'URL du stockage objet utilisé (vide : AWS). | Optional: {} | |
region string | Region est la région du stockage utilisé. | Optional: {} | |
forcePathStyle boolean | ForcePathStyle indique si l'URL du bucket est construite avec le nom du bucket dans le chemin. | Optional: {} | |
prefix string | Prefix est le dossier servi : choisi par la plateforme en stockage default, spec.s3.prefix en custom. | Optional: {} | |
credentialsSecret string | CredentialsSecret est la référence des clés d'accès utilisées pour lire votre stockage (stockage custom uniquement). | Optional: {} | |
tls CDNSiteTLSStatus | TLS décrit le certificat HTTPS du site (mode et validation). | Optional: {} | |
conditions Condition array | Conditions : DomainAllowed (domaine autorisé pour votre tenant), S3Reachable (stockage joignable), Ready (site servi). | Optional: {} |
CDNSiteS3Spec
CDNSiteS3Spec décrit votre propre stockage objet (compatible avec l'API AWS), d'où le site est servi.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
bucket string | Bucket est le nom de votre bucket. | MinLength: 1 Required: {} | |
endpoint string | Endpoint est l'URL de votre fournisseur de stockage objet (par exemple MinIO). Vide : AWS. | Optional: {} | |
region string | Region est la région de votre bucket (défaut : us-east-1). | us-east-1 | Optional: {} |
prefix string | Prefix est le dossier du bucket qui contient les fichiers du site (facultatif). | Optional: {} | |
forcePathStyle boolean | ForcePathStyle place le nom du bucket dans le chemin de l'URL plutôt que dans le nom d'hôte (requis par exemple par MinIO). | Optional: {} | |
credentialsSecretRef SecretKeyReference | CredentialsSecretRef désigne le secret de votre tenant qui contient les clés d'accès au bucket. Ce secret doit porter le label cdn.stackngo.io/s3-credentials: "true" ; un secret géré par la plateforme est refusé. | Required: {} |
LocalObjectReference
LocalObjectReference désigne un secret de votre tenant par son nom.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
name string | Name est le nom du secret. | MinLength: 1 |
CDNSiteTLSStatus
CDNSiteTLSStatus décrit le certificat HTTPS du site.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
mode string | Mode : None (site servi sans HTTPS), Custom (votre certificat, spec.tlsSecretRef), Wildcard (certificat joker partagé par tous les sites de la zone) ou Host (certificat propre au domaine du site). | Optional: {} | |
challenge string | Challenge : validation du certificat obtenu par la plateforme, dns-01 (par DNS) ou http-01 (par HTTP : le domaine doit déjà répondre depuis la plateforme). Vide pour None et Custom. | Optional: {} |
SecretKeyReference
SecretKeyReference désigne le secret de votre tenant qui contient les clés d'accès à votre stockage objet.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
name string | Name est le nom du secret, dans votre tenant. | MinLength: 1 Required: {} | |
accessKeyIDKey string | AccessKeyIDKey est la clé du secret qui contient l'identifiant de la clé d'accès (défaut : accessKeyId). | Optional: {} | |
secretAccessKeyKey string | SecretAccessKeyKey est la clé du secret qui contient la clé d'accès secrète (défaut : secretAccessKey). | Optional: {} | |
sessionTokenKey string | SessionTokenKey est la clé du secret qui contient un jeton de session, si vos clés en exigent un. | Optional: {} |
Status and conditions
| Phase | Meaning |
|---|---|
Ready | the site is served |
Pending | storage unreachable for now (missing or refused Secret, missing keys, bucket not responding); retried every minute |
Waiting | brought domain whose DNS zone is not verified yet: the site is declared, and is served only once the zone is verified (see Brought domain below) |
Invalid | domain not allowed, or storage rejected; no retry until a change |
Conflict | another older site, or another resource, already serves this domain |
A site that is not Ready is not exposed: the CDN responds 404. status.message explains
the phase; status.storage, bucket, prefix… describe the served location, status.tls the
certificate (see Certificate below).
sng kind get CDNSite docs shows these conditions as comments below the manifest; --json gives
the full status.
| Condition | True | False (reason) |
|---|---|---|
DomainAllowed | Allowed | NotAllowed: domain outside your zone and your brought domains; WaitingForDNSZone: DNS zone of the brought domain not verified yet |
S3Reachable | Reachable | Invalid (storage rejected), Unreachable (storage unreachable), SecretNotAllowed (keys Secret refused) |
Ready | Ready | Invalid, Pending, Waiting, or Conflict (mirrors the phase) |
status.message | Phase | Action |
|---|---|---|
domaine "…" non autorisé … | Invalid | use a name of your zone or of a brought domain of your tenant |
en attente d'une zone DNS vérifiée couvrant "…" (followed by zone existante en phase … if the zone already exists) | Waiting | nothing to run: check the domain's zone, see Bring a domain |
domaine déjà utilisé par un autre site | Conflict | another name; another tenant's site is not named |
domaine déjà servi par une autre ressource | Conflict | another name; the competing resource is not named |
Secret "…" introuvable … | Pending | create the referenced Secret |
secret TLS "…" introuvable : site servi sans HTTPS | Ready | create the tlsSecretRef secret, or remove tlsSecretRef |
Secret "…" refusé : label cdn.stackngo.io/s3-credentials="true" absent | Pending (SecretNotAllowed) | add the label to the Secret |
Secret "…" refusé : type "…" non accepté (Opaque attendu) | Pending (SecretNotAllowed) | recreate the Secret with type Opaque |
Secret "…" refusé : Secret géré par … | Pending (SecretNotAllowed) | create a Secret dedicated to the bucket keys |
Secret "…" : clés d'accès … manquantes | Pending | check the keys, or accessKeyIDKey / secretAccessKeyKey |
… injoignable avec ces clés : … | Pending | check bucket, region, endpoint, key permissions |
endpoint "…" : https obligatoire (or adresse non publique, hôte interne interdit) | Invalid | public https endpoint |
spec.s3 ne peut pas viser le bucket par défaut : retirer spec.s3 | Invalid | remove spec.s3 |
stockage par défaut non configuré … | Invalid | contact support |
What happens when you apply
Creation. The site is served as soon as it is Ready, over HTTPS under the requested
domain. If two sites request the same domain, the older one keeps it; a domain already served
by another resource (an App, for example) stays with it if it is older than the site. With your own bucket,
the platform tests access with your keys before exposing the site; a change to your keys is
taken into account within 10 minutes.
Brought domain. A site can be declared under a brought domain before its zone is ready, and the order of the sng apply commands does not matter. As long as the DNS zone is not verified, the site is in phase Waiting (DomainAllowed=False, reason WaitingForDNSZone): nothing is served, and sng cdn list shows it in the STATE column. As soon as the zone is verified, the site becomes Ready and is served, with no new command. If the zone exists but is not verified yet, the message says so with its phase.
Certificate. Without tlsSecretRef, the site automatically receives a valid HTTPS
certificate, usually within minutes of its creation, and it is renewed without intervention.
For a brought domain, as long as its zone is not delegated, the certificate is obtained name by name: the domain must already point to the platform (HTTP validation). After the name servers are delegated, the zone's wildcard certificate takes over. A single wildcard certificate (*.acme.dev.stackngo.app) covers every site placed directly
under your zone (docs.acme.dev.stackngo.app, blog.acme.dev.stackngo.app): the first site
of the zone obtains it, the next ones are served over HTTPS as soon as they are created, and
it goes away with the last one. A deeper name (v2.docs.acme.dev.stackngo.app) receives its
own certificate. With tlsSecretRef, your certificate is used instead (if it is missing, the
site is served without HTTPS and status.message says so).
status.tls shows the certificate being served:
status.tls.mode | status.tls.challenge | Certificate |
|---|---|---|
Wildcard | dns-01 | wildcard of your zone, shared by its sites, validated through DNS |
Host | dns-01 | specific to the site's domain, validated through DNS |
Custom | yours (tlsSecretRef) | |
None | none: the site is served without HTTPS |
sng kind get CDNSite docs --json returns status.tls.
Routes. Each folder serves its index.html, with or without a trailing /: /docs/intro/
and /docs/intro serve docs/intro/index.html (for /docs/intro, only if no docs/intro
file exists). The fallback only responds afterwards. A 404 is kept 30 seconds in cache.
Update. sng apply, sng cdn enable|disable. Maintenance takes effect in about 30
seconds; the cache is cleared during the transition. A change of domain re-exposes the
site under the new name; with default storage, the content is tied to the domain:
republish it (sng cdn upload).
What happens when you delete
sng cdn delete docs or sng kind delete CDNSite docs: the site is no longer served (404), its
content in default storage is deleted, and its cache is cleared. Your own bucket is
never modified. No deletionPolicy field.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
sng apply: spec.domain "…" : réservé à la plateforme ou à un autre client | name under the platform domain, outside your zone | a name of your zone or of a brought domain of your tenant |
Phase Waiting, WaitingForDNSZone | DNS zone of the brought domain not verified yet | Bring a domain; the site goes live on its own |
sng cdn upload: le site "assets" sert un bucket injecté : déposez les fichiers dans ce bucket (409) | site using spec.s3 | send the files to your bucket, then sng cdn clear-cache |
/blog/ shows the home page | index.html is only served at the root; the fallback responds | links to /blog/index.html, or a blog.html file |
A freshly published file responds 404 | 404 cached for 30 min, file sent outside sng cdn upload | sng cdn clear-cache <site> |
Phase Pending, Secret "…" introuvable | Secret missing from the tenant | see the warning about referenced Secrets |
Phase Pending, S3Reachable=False, SecretNotAllowed | keys Secret without the label cdn.stackngo.io/s3-credentials: "true", not of type Opaque, or managed by the platform | see The Secret holding your bucket keys |
Phase Conflict | domain taken by an older site, or by another resource | another site name |
sng cdn create: un site "docs" existe déjà (gra/staging) (409) | name taken in another context | another name, or work in that context |
See also
- Guides: Publish a static site, Maintenance and cache.
- CLI:
sng cdn, Resources. - Other kinds:
App(dynamic sites),DNSRecord.