Skip to main content

CDNSite

Available

Deployed and supported. The fields and commands described here are stable within version v1alpha1.

apiVersioncdn.stackngo.io/v1alpha1
kindCDNSite
Applied bysng apply (manifest) or sng cdn create, enable, disable
Related commandssng 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​

docs-site.yml
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​

docs-site.yml
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:

assets-site.yml
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
Referenced secrets

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:

docs-tls.yml
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 domain in uppercase, with a trailing dot, or that is not a full name (at least two labels);
  • a domain under 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 path that does not start with /;
  • an absolute fallback or s3.prefix, one with .., or one with a character outside A-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.endpoint over http, 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 CDNSite in your tenant, across all contexts (409);
  • a metadata.name different from the file name, a tenant specified in the manifest, an unknown field (including status).

CDNSite​

CDNSite publie un site statique sur votre domaine, servi par le CDN de la plateforme.

ChampDescriptionDéfautValidation
apiVersion stringcdn.stackngo.io/v1alpha1
kind stringCDNSite
metadata ObjectMetaSeul 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.

ChampDescriptionDéfautValidation
domain stringDomain 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 stringPath est le chemin d'URL sous lequel le site est servi (défaut : /)./Optional: {}
pathType stringPathType 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).
PrefixEnum: [Prefix Exact ImplementationSpecific]
Optional: {}
maintenanceMode booleanMaintenanceMode : le site répond 503 à toutes les requêtes ; le domaine et le certificat restent en place.falseOptional: {}
fallback stringFallback 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 CDNSiteS3SpecVotre 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 LocalObjectReferenceTLSSecretRef 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.

ChampDescriptionDéfautValidation
observedGeneration integerObservedGeneration est la dernière version du manifeste prise en compte par la plateforme.Optional: {}
phase stringPhase : Pending, Ready, Conflict ou Invalid.Optional: {}
message stringMessage explique la phase (raison d'un Invalid, d'un Conflict…).Optional: {}
storage stringStorage : default (stockage fourni par la plateforme) ou custom (votre stockage, spec.s3).Optional: {}
bucket stringBucket est le bucket d'où le site est servi.Optional: {}
endpoint stringEndpoint est l'URL du stockage objet utilisé (vide : AWS).Optional: {}
region stringRegion est la région du stockage utilisé.Optional: {}
forcePathStyle booleanForcePathStyle indique si l'URL du bucket est construite avec le nom du bucket dans le chemin.Optional: {}
prefix stringPrefix est le dossier servi : choisi par la plateforme en stockage default, spec.s3.prefix en custom.Optional: {}
credentialsSecret stringCredentialsSecret est la référence des clés d'accès utilisées pour lire votre stockage (stockage custom uniquement).Optional: {}
tls CDNSiteTLSStatusTLS décrit le certificat HTTPS du site (mode et validation).Optional: {}
conditions Condition arrayConditions : 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.

ChampDescriptionDéfautValidation
bucket stringBucket est le nom de votre bucket.MinLength: 1
Required: {}
endpoint stringEndpoint est l'URL de votre fournisseur de stockage objet (par exemple MinIO). Vide : AWS.Optional: {}
region stringRegion est la région de votre bucket (défaut : us-east-1).us-east-1Optional: {}
prefix stringPrefix est le dossier du bucket qui contient les fichiers du site (facultatif).Optional: {}
forcePathStyle booleanForcePathStyle 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 SecretKeyReferenceCredentialsSecretRef 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.

ChampDescriptionDéfautValidation
name stringName est le nom du secret.MinLength: 1

CDNSiteTLSStatus​

CDNSiteTLSStatus décrit le certificat HTTPS du site.

ChampDescriptionDéfautValidation
mode stringMode : 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 stringChallenge : 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.

ChampDescriptionDéfautValidation
name stringName est le nom du secret, dans votre tenant.MinLength: 1
Required: {}
accessKeyIDKey stringAccessKeyIDKey est la clé du secret qui contient l'identifiant de la clé d'accès (défaut : accessKeyId).Optional: {}
secretAccessKeyKey stringSecretAccessKeyKey est la clé du secret qui contient la clé d'accès secrète (défaut : secretAccessKey).Optional: {}
sessionTokenKey stringSessionTokenKey est la clé du secret qui contient un jeton de session, si vos clés en exigent un.Optional: {}

Status and conditions​

PhaseMeaning
Readythe site is served
Pendingstorage unreachable for now (missing or refused Secret, missing keys, bucket not responding); retried every minute
Waitingbrought 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)
Invaliddomain not allowed, or storage rejected; no retry until a change
Conflictanother 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.

ConditionTrueFalse (reason)
DomainAllowedAllowedNotAllowed: domain outside your zone and your brought domains; WaitingForDNSZone: DNS zone of the brought domain not verified yet
S3ReachableReachableInvalid (storage rejected), Unreachable (storage unreachable), SecretNotAllowed (keys Secret refused)
ReadyReadyInvalid, Pending, Waiting, or Conflict (mirrors the phase)
status.messagePhaseAction
domaine "…" non autorisé …Invaliduse 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)Waitingnothing to run: check the domain's zone, see Bring a domain
domaine déjà utilisé par un autre siteConflictanother name; another tenant's site is not named
domaine déjà servi par une autre ressourceConflictanother name; the competing resource is not named
Secret "…" introuvable …Pendingcreate the referenced Secret
secret TLS "…" introuvable : site servi sans HTTPSReadycreate the tlsSecretRef secret, or remove tlsSecretRef
Secret "…" refusé : label cdn.stackngo.io/s3-credentials="true" absentPending (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 … manquantesPendingcheck the keys, or accessKeyIDKey / secretAccessKeyKey
… injoignable avec ces clés : …Pendingcheck bucket, region, endpoint, key permissions
endpoint "…" : https obligatoire (or adresse non publique, hôte interne interdit)Invalidpublic https endpoint
spec.s3 ne peut pas viser le bucket par défaut : retirer spec.s3Invalidremove spec.s3
stockage par défaut non configuré …Invalidcontact 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.modestatus.tls.challengeCertificate
Wildcarddns-01wildcard of your zone, shared by its sites, validated through DNS
Hostdns-01specific to the site's domain, validated through DNS
Customyours (tlsSecretRef)
Nonenone: 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​

SymptomCauseFix
sng apply: spec.domain "…" : réservé à la plateforme ou à un autre clientname under the platform domain, outside your zonea name of your zone or of a brought domain of your tenant
Phase Waiting, WaitingForDNSZoneDNS zone of the brought domain not verified yetBring 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.s3send the files to your bucket, then sng cdn clear-cache
/blog/ shows the home pageindex.html is only served at the root; the fallback respondslinks to /blog/index.html, or a blog.html file
A freshly published file responds 404404 cached for 30 min, file sent outside sng cdn uploadsng cdn clear-cache <site>
Phase Pending, Secret "…" introuvableSecret missing from the tenantsee the warning about referenced Secrets
Phase Pending, S3Reachable=False, SecretNotAllowedkeys Secret without the label cdn.stackngo.io/s3-credentials: "true", not of type Opaque, or managed by the platformsee The Secret holding your bucket keys
Phase Conflictdomain taken by an older site, or by another resourceanother site name
sng cdn create: un site "docs" existe déjà (gra/staging) (409)name taken in another contextanother name, or work in that context

See also​