226 lines
9.5 KiB
Markdown
226 lines
9.5 KiB
Markdown
# Decap CMS: Gitea settings
|
|
|
|
Status: applied and verified for internal use on 2026-09-04. The repository
|
|
targets the permanent `cms` staging branch, the CMS application is deployed
|
|
from `main`, and `sloanops.admin` is the approved internal editor. Client or
|
|
public access remains out of scope.
|
|
|
|
The repository implementation targets these fixed public values:
|
|
|
|
| Setting | Value |
|
|
| --- | --- |
|
|
| Gitea repository | `SloanOps/3031023-xyz` |
|
|
| Decap write branch | `cms` |
|
|
| Gitea base URL | `https://git.sloanops.com` |
|
|
| Gitea API root | `https://git.sloanops.com/api/v1` |
|
|
| Stable CMS origin | `https://cms-3031023-xyz-www.ksloan90.workers.dev` |
|
|
| Production admin origin | `https://3031023.xyz` |
|
|
| Exact OAuth redirects | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` and `https://3031023.xyz/admin/` |
|
|
| Required status context | `CMS content validation / cms-content-validation (pull_request)` |
|
|
|
|
The pilot and permanent branch names are intentionally slash-free. Decap
|
|
CMS 3.15.1 inserts the configured Gitea branch directly into its tree-API URL;
|
|
a slash-named branch such as `cms/pilot` is split as an API path and its 404 is
|
|
presented as an empty collection. The permanent `cms` branch is still a
|
|
non-production staging branch: changes reach `main` only through the protected
|
|
pull-request flow.
|
|
|
|
The stable Worker alias and production admin route were confirmed. If either
|
|
origin changes, update repository configuration, CORS, and OAuth together
|
|
through review. Branch-specific preview aliases are intentionally not
|
|
registered and will return `Unregistered Redirect URI` during login.
|
|
|
|
## Runner cache settings
|
|
|
|
The Dockerized runner uses a persistent `/data` volume and the same dedicated
|
|
Docker network as its job containers. Its non-confidential cache settings are:
|
|
|
|
```yaml
|
|
cache:
|
|
enabled: true
|
|
dir: /data/cache
|
|
host: sloanops-gitea-runner
|
|
port: 8088
|
|
offline_mode: true
|
|
container:
|
|
network: sloanops-actions
|
|
```
|
|
|
|
`offline_mode` is safe here because every external `uses:` reference is pinned
|
|
to a reviewed full commit SHA. It prevents the runner from refreshing mutable
|
|
action branches on every job; npm packages remain keyed separately by
|
|
`package-lock.json` through setup-node's cache integration.
|
|
|
|
## Applied CORS policy
|
|
|
|
The tracked Gitea service in
|
|
`/home/sloan/gitRepos/SloanOps/infrastructure/docker-compose.yml` was deployed
|
|
with exact origins for the internal CMS applications. The two origins relevant
|
|
to this repository are represented by these settings:
|
|
|
|
```yaml
|
|
- GITEA__cors__ENABLED=true
|
|
- GITEA__cors__ALLOW_DOMAIN=https://cms-3031023-xyz-www.ksloan90.workers.dev,https://3031023.xyz
|
|
- GITEA__cors__METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
|
|
- GITEA__cors__MAX_AGE=10m
|
|
- GITEA__cors__ALLOW_CREDENTIALS=false
|
|
- GITEA__cors__HEADERS=Authorization,Content-Type
|
|
```
|
|
|
|
These are exact origins, not wildcards, and API requests do not carry cookies.
|
|
Do not add arbitrary Worker preview hosts or localhost. Gitea's same-origin
|
|
login page continues to use its own session cookie; Decap API calls use the
|
|
OAuth bearer token.
|
|
|
|
After an approved Gitea deployment, verify a preflight without including a
|
|
token or cookie:
|
|
|
|
```bash
|
|
curl --request OPTIONS 'https://git.sloanops.com/api/v1/version' \
|
|
--header 'Origin: https://cms-3031023-xyz-www.ksloan90.workers.dev' \
|
|
--header 'Access-Control-Request-Method: GET' \
|
|
--header 'Access-Control-Request-Headers: authorization,content-type'
|
|
```
|
|
|
|
The response must allow only the configured origin, requested method, and
|
|
headers. Repeat with an unrelated origin and confirm that Gitea does not grant
|
|
it cross-origin access. Also verify normal Git, web UI, API, and Actions
|
|
behavior before continuing.
|
|
|
|
## OAuth application
|
|
|
|
The Gitea OAuth2 application was created after the stable preview origin and
|
|
CORS response were verified:
|
|
|
|
- Application name: `3031023.xyz Decap CMS`.
|
|
- Confidential client: **unchecked**. The browser client must use PKCE.
|
|
- Redirect URIs: exactly
|
|
`https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` and
|
|
`https://3031023.xyz/admin/`, including trailing slashes.
|
|
- Do not add wildcards, localhost, the journal hostname, or branch-specific
|
|
Worker preview aliases as additional redirects.
|
|
- Copy only the public client ID into `backend.app_id` in
|
|
`public/admin/config.yml` through a reviewed commit.
|
|
- Do not store or use a client secret in the repository, Actions, Cloudflare,
|
|
or Decap configuration.
|
|
|
|
The CMS account still needs repository write access; the branch rules below
|
|
are the security boundary that reduces that write access to pilot content and
|
|
media paths.
|
|
|
|
## Applied branch protection rules
|
|
|
|
Create both rules before granting a CMS/client account write access or testing
|
|
login. Gitea uses only the first matching rule, so the order is material.
|
|
|
|
### Priority 1: production `main`
|
|
|
|
| Gitea control | Required value |
|
|
| --- | --- |
|
|
| Protected branch pattern | `main` |
|
|
| Priority | `1` (first/highest) |
|
|
| Push | Disabled for everyone; no push allowlist |
|
|
| Force push | Disabled |
|
|
| Required approvals | `1` |
|
|
| Approval allowlist | Enabled; only the named SloanOps operator user/team |
|
|
| Dismiss stale approvals | Enabled |
|
|
| Ignore stale approvals | Disabled |
|
|
| Block rejected reviews | Enabled |
|
|
| Block outstanding official review requests | Enabled |
|
|
| Block outdated pull requests | Enabled |
|
|
| Status checks | Enabled |
|
|
| Status check pattern | `CMS content validation / cms-content-validation (pull_request)` |
|
|
| Merge allowlist | Enabled; only the named SloanOps operator user/team |
|
|
| Administrators must follow protection | Enabled |
|
|
| Protection bypass allowlist | Disabled |
|
|
|
|
Leave both file-pattern fields empty on this rule. Direct pushes are already
|
|
disabled, and executable or configuration changes must remain possible only in
|
|
a separate operator-authored, reviewed pull request. The fallback rule prevents
|
|
CMS users from placing those files on any source branch.
|
|
|
|
This rule means every `main` change arrives through a current pull request,
|
|
passes the one secret-free job that validates the CMS contract and builds and
|
|
verifies both sites, receives a current SloanOps approval, and is merged by a
|
|
SloanOps operator. A CMS/client account must not be added to the approval,
|
|
merge, administrator, or bypass allowlists.
|
|
|
|
### Priority 2: all-branch fallback
|
|
|
|
| Gitea control | Required value |
|
|
| --- | --- |
|
|
| Protected branch pattern | `*` |
|
|
| Priority | `2` (after `main`) |
|
|
| Push | Disabled, except the unprotected file patterns below |
|
|
| Force push | Disabled |
|
|
| Unprotected file patterns | Exact semicolon-separated value below |
|
|
| Protected file patterns | Empty; every path omitted from the unprotected list remains protected |
|
|
| Protection bypass allowlist | Enabled only for the named SloanOps operator user/team |
|
|
| CMS/client account | Write access, but absent from every allowlist |
|
|
|
|
Do not assume a local Git identity can use the operator bypass. A direct
|
|
protected-file push was rejected during the final Journal repair sync, and the
|
|
successful path was a short branch followed by PR #11 into `cms`. Prefer that
|
|
reviewable path for all operator-authored CMS code and configuration changes.
|
|
|
|
Enter this exact single value in **Unprotected file patterns**:
|
|
|
|
```text
|
|
sites/www/content/**;sites/blog/content/**;public/images/uploads/*.jpg;public/images/uploads/*.jpeg;public/images/uploads/*.png;public/images/uploads/*.webp;public/images/uploads/*.avif
|
|
```
|
|
|
|
Do not add a nested upload glob. The current Decap configuration writes media
|
|
directly into the flat uploads directory, and the local verifier rejects nested
|
|
media paths. Gitea file patterns are case-insensitive; CI verifies actual file
|
|
signatures and a 25 MiB source safety ceiling before deployment. The build
|
|
re-encodes the deployed copy to at most 1920 pixels and 500 KiB and strips
|
|
embedded metadata.
|
|
|
|
Everything not listed is protected from CMS/client pushes on `cms` and
|
|
on every differently named branch, including:
|
|
|
|
```text
|
|
.gitea/**
|
|
astro.config.mjs
|
|
package.json
|
|
package-lock.json
|
|
schemas/**
|
|
scripts/**
|
|
src/**
|
|
wrangler/**
|
|
sites.json
|
|
sites/*/site.json
|
|
public/admin/**
|
|
public/_headers
|
|
public/images/*
|
|
```
|
|
|
|
The final `public/images/*` line describes the effect of omission, not an
|
|
additional Gitea field value: existing approved images stay protected while
|
|
the explicitly listed `public/images/uploads/*.<raster>` paths remain writable.
|
|
Workflows, source, scripts, package files, schemas, manifests, Wrangler
|
|
configuration, `site.json` files, and the CMS application therefore cannot be
|
|
altered by choosing another branch name.
|
|
|
|
## Routine verification and promotion
|
|
|
|
1. Run `npm ci`, `npm run verify:cms`, and both site build/verifier pairs.
|
|
2. For CMS application/configuration work, branch from current `cms`, open a
|
|
pull request back into `cms`, and review the no-index version previews.
|
|
The required CMS status is scoped to pull requests into `main`, so the local
|
|
full check is mandatory before this staging merge.
|
|
3. Open `cms` to `main` and confirm Gitea reports the exact required status
|
|
context above and blocks merge without its successful result and current
|
|
SloanOps approval.
|
|
4. Merge deliberately, then confirm each affected production Worker workflow
|
|
succeeds and smoke-test the public routes.
|
|
5. Merge `main` back into `cms` through another protected-branch pull request
|
|
only when its tree contains file changes missing from `cms`. Do not create a
|
|
history-only sync when the trees already match.
|
|
6. Confirm previews remain no-index and never expand OAuth/CORS for a temporary
|
|
branch alias.
|
|
|
|
Do not treat a Decap UI restriction as authorization. Gitea's first-match
|
|
branch rules are the enforcement boundary, and the pull-request build is the
|
|
content/media integrity boundary.
|