werkcrappit's code city page links a CodeCharta viewer with the URL of the cc.json beside it.
Served by Werkator, the viewer at javagil.de could not load it: the browser blocks a script from reading another site's response unless that site allows it with Access-Control-Allow-Origin.
CodeCharta reported only Http failure response … 0 Unknown Error and loaded its sample files instead.
Non-Goals
Credentials: no Access-Control-Allow-Credentials, so a private repository's artifacts stay unreadable from other sites.
Preflight handling: a plain GET without own headers needs none.
Other routes than artifact files: pages and the API keep answering without CORS.
The Scenarios
Scenario#96.01: an artifact file answers an allowed origin
… so that a viewer on another host, such as CodeCharta at javagil.de, loads a code city straight from Werkator.
Givenserver.corsAllowedOrigins in the instance file lists https://javagil.de
When the browser fetches an artifact file of a public repository with Origin: https://javagil.de
Then the response carries Access-Control-Allow-Origin: https://javagil.de and Vary: Origin
And no Access-Control-Allow-Credentials, so a token or cookie never reaches the other origin
ServerConfig.corsAllowedOrigins, a list of exact origins, empty by default.
The server section is instance-level: with an instance file, a repository's copy is ignored with a warning, and a branch layer never carries it.
Without an instance file, the repository's committed file is the instance's, as for every server key.
ArtifactFileController answers both artifact routes, by key and the permanent branch URL, with the request's Origin when it is listed, and always with Vary: Origin, so a cache never hands one site's answer to another.
The commented init template and docs/configuration.md name the key.
Open Questions
mih09 needs server.corsAllowedOrigins: ["https://javagil.de", "https://codecharta.com"] in ~/.werkator.yml; read per request, no restart needed.
## The Problem
werkcrappit's code city page links a CodeCharta viewer with the URL of the `cc.json` beside it.
Served by Werkator, the viewer at javagil.de could not load it: the browser blocks a script from reading another site's response unless that site allows it with `Access-Control-Allow-Origin`.
CodeCharta reported only `Http failure response … 0 Unknown Error` and loaded its sample files instead.
## Non-Goals
- Credentials: no `Access-Control-Allow-Credentials`, so a private repository's artifacts stay unreadable from other sites.
- Preflight handling: a plain `GET` without own headers needs none.
- Other routes than artifact files: pages and the API keep answering without CORS.
## The Scenarios
#### Scenario#96.01: an artifact file answers an allowed origin
… so that a viewer on another host, such as CodeCharta at javagil.de, loads a code city straight from Werkator.
- **Given** `server.corsAllowedOrigins` in the instance file lists `https://javagil.de`
- **When** the browser fetches an artifact file of a public repository with `Origin: https://javagil.de`
- **Then** the response carries `Access-Control-Allow-Origin: https://javagil.de` and `Vary: Origin`
- **And** no `Access-Control-Allow-Credentials`, so a token or cookie never reaches the other origin
##### Verified by
- [ArtifactFileControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/ArtifactFileControllerRestTest.kt) `an artifact file answers an allowed origin, without credentials`
#### Scenario#96.02: no header for other origins or without the setting
… so that Werkator shares nothing with a website the instance did not name.
- **Given** `server.corsAllowedOrigins` lists `https://javagil.de`
- **When** the browser fetches an artifact file with `Origin: https://evil.example`, or without `Origin`
- **Then** the response carries no `Access-Control-Allow-Origin`
- **And** without the setting, the default, no response carries one, as before
##### Verified by
- [ArtifactFileControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/ArtifactFileControllerRestTest.kt) `another origin, or none, gets no CORS header`
- [ConfigLoaderUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderUnitTest.kt) `without a setting no origin may read artifacts`
#### Scenario#96.03: a repository cannot widen the setting
… so that only the operator of an instance decides which websites may read its artifacts.
- **Given** a repository's `.werkator.yml`, committed or on a branch, sets `server.corsAllowedOrigins`
- **Then** that value is ignored, with the same warning as other `server` keys outside the instance file
- **And** a private repository still answers 404 without the control token, `Origin` or not
##### Verified by
- [ConfigLoaderUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderUnitTest.kt) `the origins allowed to read artifacts are the instance's`
- [ArtifactFileControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/ArtifactFileControllerRestTest.kt) `a private repository stays hidden to an allowed origin`
## The Solution
- `ServerConfig.corsAllowedOrigins`, a list of exact origins, empty by default.
The `server` section is instance-level: with an instance file, a repository's copy is ignored with a warning, and a branch layer never carries it.
Without an instance file, the repository's committed file is the instance's, as for every `server` key.
- `ArtifactFileController` answers both artifact routes, by key and the permanent branch URL, with the request's `Origin` when it is listed, and always with `Vary: Origin`, so a cache never hands one site's answer to another.
- The commented `init` template and `docs/configuration.md` name the key.
## Open Questions
- mih09 needs `server.corsAllowedOrigins: ["https://javagil.de", "https://codecharta.com"]` in `~/.werkator.yml`; read per request, no restart needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The Problem
werkcrappit's code city page links a CodeCharta viewer with the URL of the
cc.jsonbeside it.Served by Werkator, the viewer at javagil.de could not load it: the browser blocks a script from reading another site's response unless that site allows it with
Access-Control-Allow-Origin.CodeCharta reported only
Http failure response … 0 Unknown Errorand loaded its sample files instead.Non-Goals
Access-Control-Allow-Credentials, so a private repository's artifacts stay unreadable from other sites.GETwithout own headers needs none.The Scenarios
Scenario#96.01: an artifact file answers an allowed origin
… so that a viewer on another host, such as CodeCharta at javagil.de, loads a code city straight from Werkator.
server.corsAllowedOriginsin the instance file listshttps://javagil.deOrigin: https://javagil.deAccess-Control-Allow-Origin: https://javagil.deandVary: OriginAccess-Control-Allow-Credentials, so a token or cookie never reaches the other originVerified by
an artifact file answers an allowed origin, without credentialsScenario#96.02: no header for other origins or without the setting
… so that Werkator shares nothing with a website the instance did not name.
server.corsAllowedOriginslistshttps://javagil.deOrigin: https://evil.example, or withoutOriginAccess-Control-Allow-OriginVerified by
another origin, or none, gets no CORS headerwithout a setting no origin may read artifactsScenario#96.03: a repository cannot widen the setting
… so that only the operator of an instance decides which websites may read its artifacts.
.werkator.yml, committed or on a branch, setsserver.corsAllowedOriginsserverkeys outside the instance fileOriginor notVerified by
the origins allowed to read artifacts are the instance'sa private repository stays hidden to an allowed originThe Solution
ServerConfig.corsAllowedOrigins, a list of exact origins, empty by default.The
serversection is instance-level: with an instance file, a repository's copy is ignored with a warning, and a branch layer never carries it.Without an instance file, the repository's committed file is the instance's, as for every
serverkey.ArtifactFileControlleranswers both artifact routes, by key and the permanent branch URL, with the request'sOriginwhen it is listed, and always withVary: Origin, so a cache never hands one site's answer to another.inittemplate anddocs/configuration.mdname the key.Open Questions
server.corsAllowedOrigins: ["https://javagil.de", "https://codecharta.com"]in~/.werkator.yml; read per request, no restart needed.🤖 Generated with Claude Code