diff options
| author | Leitwolf11 | 2026-10-03 18:21:43 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-03 18:21:43 +0300 |
| commit | 8738fc4e138745f2d065cf54c25426e3d37c119d (patch) | |
| tree | 65871899976b4e6f0f04d55a50fa209d92b855e1 | |
| parent | 710dc90b0503cd1de1ae6159e01fb22d1b336aba (diff) | |
| download | website-docs/current-state.tar.gz | |
Bring the documentation up to the published site and fill its gapsdocs/current-state
The documents still described a site in maintenance with two languages and
a forum page with a language menu. They now describe what is published, and
three documents are new.
New, under Documents/:
- README.md: index of the documentation.
- ARCHITECTURE.md: the frontend, the backend and the forum page, what each
serves, what the site deliberately does not contain, and how search
engines and crawlers are treated.
- LOCALIZATION.md: the four languages, how a page picks one, what counts as
a visitor's choice, how Hebrew text is laid out, where the text lives and
how to add a language.
- DEVELOPMENT.md: requirements, the commands of each part, the dependency
locks and checksums, the tests and what CI enforces.
Updated:
- README.md: the site is public; four languages and the browser-language
default; a documentation section; the Dokka gate.
- CONTENT.md: the forum notice without a language menu; translation rules
for four languages, including preferring the term a reader would use over
a literal one; the crawler rules as an owner decision.
- OPERATIONS.md: the live route table; the release steps for the directory
layout on the server, with the SELinux labelling order that caused an
outage once; cache keys for the forum files and CDN caching; maintenance
mode as a fallback instead of the current state; rollback by directory;
checksum regeneration with --refresh-dependencies; the post-release check
for four languages and right-to-left text.
No source file changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | Documents/ARCHITECTURE.md | 102 | ||||
| -rw-r--r-- | Documents/CONTENT.md | 51 | ||||
| -rw-r--r-- | Documents/DEVELOPMENT.md | 113 | ||||
| -rw-r--r-- | Documents/LOCALIZATION.md | 95 | ||||
| -rw-r--r-- | Documents/OPERATIONS.md | 147 | ||||
| -rw-r--r-- | Documents/README.md | 16 | ||||
| -rw-r--r-- | README.md | 32 |
7 files changed, 492 insertions, 64 deletions
diff --git a/Documents/ARCHITECTURE.md b/Documents/ARCHITECTURE.md new file mode 100644 index 0000000..c11cfea --- /dev/null +++ b/Documents/ARCHITECTURE.md @@ -0,0 +1,102 @@ +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 --> + +# Architecture + +The repository has three independent parts. Each is built and deployed as its own artifact, and none of them stores +anything about a visitor on a server. + +| Part | Technology | Artifact | Serves | +| --- | --- | --- | --- | +| `frontend/` | Vue 3, Vite, TypeScript | static files in `frontend/dist/` | `xsharp-lang.xyz` | +| `backend/` | Kotlin on JVM 25, Spring Boot | one executable jar | `xsharp-lang.xyz/api/v1/site` | +| `forum/` | one HTML page with plain CSS and JavaScript | the five files as they are | `forum.xsharp-lang.xyz` | + +```text +browser + | HTTPS + v +Nginx + |-- xsharp-lang.xyz static files of the Vue build; unknown paths fall back to index.html + |-- xsharp-lang.xyz/api/v1/site -> http://127.0.0.1:5086 (Spring) + |-- forum.xsharp-lang.xyz five named static files; every other path is 404 + `-- www, docs, spec, viget permanent redirects +``` + +## Frontend + +A single-page application with three routes, defined in `frontend/src/main.ts`: + +| Route | Page | Source | +| --- | --- | --- | +| `/` | why the language exists, and its current state | `pages/HomePage.vue` | +| `/docs/overview` | the design direction and the compiler stages | `pages/OverviewPage.vue` | +| `/docs/getting-started` | building the compiler from its repository | `pages/GettingStartedPage.vue` | + +Any other path redirects to `/`. The router uses history URLs, so the server must answer unknown paths with +`index.html`. + +The rest of the frontend is small: + +- `App.vue` is the shell: header, navigation, language menu, theme button and footer. +- `locales.ts` holds the list of languages and the pure rules for choosing one. It touches neither the page nor + storage, which is why the tests can import it directly. +- `preferences.ts` holds the current language and theme as reactive values, applies them to the page, and stores a + choice in the browser. +- `seo.ts` sets the title, description, social metadata and canonical URL of each route in each language. +- `styles/site.css` is the only stylesheet. Fira Sans is served from the site itself; there is no request to a + third-party font service. + +Each page keeps its text next to its template, one object per language. See [Localization](LOCALIZATION.md). + +## Backend + +The backend is deliberately tiny. It has two endpoints: + +| Endpoint | Returns | +| --- | --- | +| `GET /api/v1/site` | the name, the development phase, the fallback locale, the published locales and whether the forum is open | +| `GET /health` | an empty 200 for the local service manager; not proxied to the public | + +It binds to loopback only. It has no database, no account, no package and no registry endpoint, and its tests assert +that those routes are absent. The frontend renders without it; nothing on the three pages depends on the endpoint +today. + +Nginx proxies exactly `/api/v1/site`. Every other `/api/` path is answered with 404 by Nginx, so adding an endpoint +to the backend does not publish it. + +## Forum page + +`forum.xsharp-lang.xyz` is a notice that the forum is not open yet. It is not a forum: there are no accounts, no +posts and no service behind it. The page shows its one notice in the language of the browser and offers a theme +button; it has no language menu and stores no language. It is marked `noindex` in the page and in the response +header. + +The page is independent of the Vue application. It shares no code with it, and its strict content security policy +allows only its own script, stylesheet and two images. + +## What the site does not contain + +- **Accounts and packages.** Progmasoft accounts and the ViGet package registry belong to the Progmaweb repository + and live on `progmasoft.com` hosts. This site links to neither a sign-in nor a registry. +- **A legacy redirect only.** `viget.xsharp-lang.xyz` redirects permanently to `viget.progmasoft.com`, keeping path + and query. That redirect is routing compatibility, not a feature of this site. +- **Visitor data.** The language and theme a visitor chose are kept in the browser's local storage. The site sets no + cookie and sends nothing about the visitor to the backend. + +## Search engines and crawlers + +- `sitemap.xml` lists exactly the three routes above. +- Every route has its own title, description and canonical URL. A language is a preference, not a second URL, so + there are no `hreflang` alternatives. +- `robots.txt` names the crawlers the site has a rule for. Search and answer-retrieval crawlers are allowed. Among + the crawlers that collect training data, Google-Extended and ClaudeBot are allowed and GPTBot is disallowed; the + file is the authority for the full list. +- The initial HTML carries the English metadata of the homepage. The metadata of the other routes and languages is + set by JavaScript after the page loads. + +## Security headers + +Nginx sends a content security policy that allows only same-origin resources, plus HSTS, `X-Content-Type-Options`, +`X-Frame-Options: DENY` and a referrer policy. The policy has no exception for third-party scripts; a script injected +by an intermediary, such as an analytics beacon added by a CDN, is blocked by the browser. diff --git a/Documents/CONTENT.md b/Documents/CONTENT.md index d9d43bb..031ff1d 100644 --- a/Documents/CONTENT.md +++ b/Documents/CONTENT.md @@ -93,22 +93,38 @@ management flow to link from this guide. The placeholder may say the forum will come later, once the community is ready. It must not imply that an account can be created now or that -discussion posts are stored elsewhere. Its default is English and dark, -with German, Russian and light options for consistency. Its noindex status applies +discussion posts are stored elsewhere. It shows one notice, in the language +of the browser, with English as the fallback; it has no language menu. Its +theme defaults to dark, with a light option. Its noindex status applies both in HTML and at the server boundary. ## Translation rules -`en-US` is the default and must carry complete content. `de-DE` and `ru-RU` -should express the same meaning, not mechanically mirror sentence length. Keep -technical spellings such as `Visual.XSharp.kts`, `CorePrep`, `Xpp`, and -`Xmm` stable. Translate navigation labels, help text, figure descriptions, -and accessible names. When adding a paragraph or an interaction, add every -language in the same change so a locale switch never leaves mixed copy. +`en-US` is the fallback and must carry complete content. `de-DE`, `ru-RU` +and `he-IL` should express the same meaning, not mechanically mirror sentence +length. Keep technical spellings such as `Visual.XSharp.kts`, `CorePrep`, +`Xpp`, and `Xmm` stable. Translate navigation labels, help text, figure +descriptions, and accessible names. When adding a paragraph or an +interaction, add every language in the same change so a locale switch never +leaves mixed copy. + +Prefer the term a developer who reads that language would use over a literal +rendering. A literal word can mean something else: in Hebrew, the literal +words for "native" and "lowering" read as "local" and "download". Where no +settled term exists, keep the English term in Latin letters. Avoid forms of +address that assume the reader's gender. + +Hebrew text runs right to left, and the page layout does not mirror. Follow +`Visual X#` and `C#` with a left-to-right mark in Hebrew strings so the `#` +stays with the name. [Localization](LOCALIZATION.md) describes the mechanics +and how a page picks its language. + +A translation should be read by a native speaker before it is published. The +Hebrew text has not had that review yet. The URL is shared between language preferences. Do not emit inaccurate -`hreflang` alternatives implying separate German or Russian URLs. The document `lang` -attribute and browser metadata should follow the selected locale. The +`hreflang` alternatives implying separate URLs per language. The document +`lang` attribute and browser metadata should follow the selected locale. The canonical URL should remain the supported route without tracking query parameters. @@ -150,9 +166,15 @@ has its own title, description, social title/description, and canonical URL when the application runs. The HTML shell has English home metadata so it does not present a blank title before JavaScript initializes. -Opening the public site later is a separate decision from writing this -content. Until then, main-domain 503 is expected and should not be -misdiagnosed as a broken frontend build. +`robots.txt` carries an explicit rule for each crawler the site has a +position on. Crawlers that index pages for search or fetch a page to answer +a question are allowed. Crawlers that collect training data are decided one +by one: Google-Extended and ClaudeBot are allowed, GPTBot is disallowed. +Changing one of those rules is a policy decision of the project owner, not a +cleanup. + +When the main domain is switched to its maintenance configuration, a 503 is +expected and should not be misdiagnosed as a broken frontend build. ## Review checklist @@ -161,7 +183,8 @@ Before approving a copy change, check: - Is every language claim grounded in the public Spec or compiler tests? - Does the wording say “designed to” where implementation is incomplete? - Does the command match the latest CLI and toolchain guide? -- Does the German version preserve meaning and technical identifiers? +- Do the German, Russian and Hebrew versions preserve meaning and technical + identifiers? - Are internal notes and private file paths absent from the public page? - Are homepage and docs links still valid after repository movement? - Are source attribution and the Visual X# leopard identity intact? diff --git a/Documents/DEVELOPMENT.md b/Documents/DEVELOPMENT.md new file mode 100644 index 0000000..4781e76 --- /dev/null +++ b/Documents/DEVELOPMENT.md @@ -0,0 +1,113 @@ +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 --> + +# Development + +## Requirements + +| Tool | Version | Used for | +| --- | --- | --- | +| Node.js | 22.18 or newer; CI uses 24 | the frontend and all JavaScript tests | +| npm | the one that ships with Node.js | frontend dependencies | +| Temurin JDK | 25 | the backend | +| Gradle | 9.6.0, the version CI installs | the backend build; the repository has no Gradle wrapper | + +## Frontend + +```text +cd frontend +npm ci +npm run dev # development server +npm run check # vue-tsc type check +npm test # Node tests +npm run format:check # Prettier, also over ../forum +npm run build # type check and production build into dist/ +``` + +`npm run format` rewrites the files Prettier checks. The development server proxies `/api` to +`http://127.0.0.1:5080`, the default port of a locally started backend. + +`.npmrc` refuses install scripts of dependencies that were not reviewed. A new dependency with an install script +fails `npm ci` until its exact version is allowed in `package.json`. + +## Backend + +```text +cd backend +gradle test bootJar dokkaGenerate --no-daemon +gradle bootRun --no-daemon # serves on 127.0.0.1:5080 +``` + +- `test` runs the Spring tests. +- `bootJar` builds `build/libs/visual-xsharp-website-api-1.0.0.jar`. +- `dokkaGenerate` builds the API documentation and is a gate: a public declaration without KDoc, a broken link or an + unresolved reference fails the build. + +### Dependency locks and checksums + +The backend pins every resolved version in `gradle.lockfile` and the SHA-256 of every downloaded file in +`gradle/verification-metadata.xml`. After changing a dependency or a plugin, regenerate both and review the diff: + +```text +gradle dependencies --write-locks --no-daemon +gradle --refresh-dependencies --write-verification-metadata sha256 help test jacocoTestReport bootJar dokkaGenerate --no-daemon +``` + +`--refresh-dependencies` matters. With a warm local cache Gradle does not download every metadata file again and so +does not record it; the build then passes locally and fails verification on a clean CI machine. + +A checksum is an integrity pin, not a trust review. Confirm the version and the repository of an artifact before +accepting its checksum. + +## Forum page + +`forum/` has no build step. Open `forum/index.html` through any static file server, for example: + +```text +python -m http.server 3022 --bind 127.0.0.1 --directory forum +``` + +Its script is tested by `frontend/tests/forum.test.mjs`, which runs it against a minimal fake document. To see the +notice in another language, change the preferred language of the browser. + +## Tests + +All JavaScript tests run with `npm test` in `frontend/`. + +| File | Asserts | +| --- | --- | +| `locales.test.mjs` | the published locales, browser-language selection, the English fallback, that a stored choice wins, text direction, and when a change is remembered | +| `seo.test.mjs` | the title, description and canonical URL of every route in every language | +| `forum.test.mjs` | the notice in each language, the fallback, the absence of a language menu, and the theme button | +| `site-integrity.test.mjs` | sitemap and router agreement, forum non-indexing, logo and social image shape, absence of retired links, pinned workflow actions, and that the Nginx and systemd files agree with each other | + +Two tests in `site-integrity.test.mjs` compare the site with a checkout of the compiler repository next to this one +and skip themselves when it is absent. + +The backend tests assert the content of `/api/v1/site`, the empty health response, that the API is read-only, and +that account and registry routes do not exist. + +A change to a page is not verified by these tests alone. Look at it in a browser: all four languages, both themes, a +narrow window, and keyboard focus. + +## Continuous integration + +| Job | Enforces | +| --- | --- | +| `frontend` | `npm ci`, `npm audit` at moderate severity, type check, tests with coverage, format check, build | +| `backend` | tests with coverage, the jar, and the Dokka documentation gate | +| `frontend-coverage`, `backend-coverage` | upload coverage to Codecov | +| `dependency-review` | reviews dependency changes of a pull request | +| CodeQL | static analysis of the workflows, TypeScript and Kotlin | + +Only the coverage jobs receive an OIDC token; the jobs that install and build do not, and pull requests from forks +skip the upload. Workflow actions are pinned to commit hashes, and a test fails when one is not. + +`main` is protected: changes arrive through pull requests with green checks. + +## Conventions + +- Public documents, comments, commit messages and pull request text are written in American English. +- Every file carries the SPDX header of the repository; `REUSE.toml` covers the formats that cannot hold a comment. +- A user-visible string is added to every locale in the same change. See [Localization](LOCALIZATION.md). +- What the pages may claim about the language is governed by the [content contract](CONTENT.md). diff --git a/Documents/LOCALIZATION.md b/Documents/LOCALIZATION.md new file mode 100644 index 0000000..d94e299 --- /dev/null +++ b/Documents/LOCALIZATION.md @@ -0,0 +1,95 @@ +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 --> + +# Localization + +The language site is published in four languages. + +| Locale | Language | Menu label | Text direction | +| --- | --- | --- | --- | +| `en-US` | English | EN | left to right | +| `de-DE` | German | DE | left to right | +| `ru-RU` | Russian | RU | left to right | +| `he-IL` | Hebrew | HE | right to left | + +A page has one URL in every language. The language is a preference kept in the browser. + +## How a page picks its language + +The rules are pure functions in `frontend/src/locales.ts`: + +1. **The visitor's choice.** A language stored under `vxs-locale-choice` in local storage always wins. +2. **The browser.** Without a stored choice, the first entry of `navigator.languages` whose primary language is + published is used. `de-AT` selects German, and `iw`, the former code for Hebrew, selects Hebrew. +3. **The fallback.** A browser that prefers no published language gets `en-US`. + +| Browser languages | Page language | +| --- | --- | +| `he-IL`, `en-US` | Hebrew | +| `de-AT`, `en` | German | +| `tr-TR`, `de` | German, the first preference that is published | +| `tr-TR`, `fr` | English, the fallback | +| none reported | English | + +## What counts as a choice + +A language becomes the stored choice only when a visitor changes it in the menu. + +- Loading a page never stores anything. A visitor who never used the menu keeps following the browser, also after + the browser's language changes. +- A change is stored only after the visitor has acted on the page (`navigator.userActivation`). A change that + reaches the page before anyone clicked, tapped or typed switches the language but is not remembered. A browser + without that API cannot tell, and there a change counts as a choice. + +Earlier versions wrote the language under `vxs-locale` on every visit, which made a real choice indistinguishable +from the former English default. That key is no longer read. + +## The forum page + +The forum notice has no language menu. `forum/site.js` shows its one notice in the first browser language that has a +translation and in English otherwise, and stores nothing about language. It keeps its own theme key, +`vxs-forum-theme`. + +## Hebrew and text direction + +Hebrew does not mirror the page. The layout, the navigation and code keep their left-to-right arrangement; only text +follows the language. + +- The root element carries `data-text-direction="rtl"`, not `dir="rtl"`. +- Headings and paragraphs run right to left and align to the right. +- Links, buttons and list items take the direction of their own first letter, so a product name on its own still + reads left to right. +- Code blocks stay left to right. +- The onward arrow comes from `forwardArrow` and points left in Hebrew. +- A left-to-right mark (``) follows `Visual X#` and `C#` in Hebrew strings. Without it the `#` jumps to the + other side of the name inside right-to-left text. + +The rules are at the end of `frontend/src/styles/site.css` and `forum/site.css`. + +## Where the text lives + +| Text | File | +| --- | --- | +| header, navigation, footer | `frontend/src/App.vue` | +| each page | the `<script setup>` block of its file under `frontend/src/pages/` | +| titles and descriptions | `frontend/src/seo.ts` | +| forum notice | `forum/site.js` | + +Each of these holds one object per locale with the same keys. TypeScript checks the page objects against the list of +locales, so a page that lacks a language does not compile. + +## Adding a language + +1. In `locales.ts`, add the locale to `supportedLocales`, a label to `localeLabels` and its primary language to + `localeByLanguage`. If it is written right to left, extend `textDirection`. +2. Add the locale to every text object listed above, and to the notice in `forum/site.js`. +3. Add the locale to `supportedLocales` in `backend/.../SiteController.kt` and to its test. +4. Extend the tests: the list in `locales.test.mjs`, a metadata case in `seo.test.mjs` and a notice case in + `forum.test.mjs`. +5. When the forum files change, change the `?v=` value on its stylesheet and script in `forum/index.html`, so that + a cached copy of the old files is not paired with the new page. +6. Run the checks in [Development](DEVELOPMENT.md) and read all three pages and the forum notice in the new language + in a browser, in both themes and at a narrow width. + +The [content contract](CONTENT.md) has the translation rules. Have a native speaker read the text before it is +published; the Hebrew text has not had that review yet. diff --git a/Documents/OPERATIONS.md b/Documents/OPERATIONS.md index 96baa57..3c3bff9 100644 --- a/Documents/OPERATIONS.md +++ b/Documents/OPERATIONS.md @@ -12,17 +12,22 @@ compatibility, but it does not make package functions part of this website. | Host and path | Owner | Current treatment | | --- | --- | --- | -| `xsharp-lang.xyz/` | Vue language site | Maintenance response until approval | -| `xsharp-lang.xyz/docs/overview` | Vue language site | Maintenance response until approval | -| `xsharp-lang.xyz/docs/getting-started` | Vue language site | Maintenance response until approval | -| `xsharp-lang.xyz/api/v1/site` | Spring language API | Not exposed during maintenance | +| `xsharp-lang.xyz/` | Vue language site | Live | +| `xsharp-lang.xyz/docs/overview` | Vue language site | Live | +| `xsharp-lang.xyz/docs/getting-started` | Vue language site | Live | +| `xsharp-lang.xyz/api/v1/site` | Spring language API | Live; the only proxied API path | | `forum.xsharp-lang.xyz/` | Static placeholder | Live; noindex | +| `www`, `docs`, `spec` subdomains | Redirect | Permanent redirect to `xsharp-lang.xyz` | | `viget.xsharp-lang.xyz/*` | Legacy redirect | Permanent redirect to the new package host | +The main domain is served with `ops/nginx/xsharp.conf`. While it is switched to +`ops/nginx/xsharp-maintenance.conf`, the three language routes answer 503 and +`/api/v1/site` is not exposed. + Do not use a catch-all `/api/` proxy for this service. The production Nginx configuration proxies exactly `/api/v1/site`; other `/api/` paths return 404. The Spring process binds only to `127.0.0.1`. Local runs default to port 5080; -the checked-in systemd unit sets `PORT=5086`, matching the future Nginx proxy. +the checked-in systemd unit sets `PORT=5086`, matching the Nginx proxy. Its `/health` endpoint is for local process checks, not an advertised public route. @@ -41,9 +46,12 @@ npm run build Run the backend gates from `backend/` with Temurin JDK 25: ```text -gradle test bootJar --no-daemon +gradle test bootJar dokkaGenerate --no-daemon ``` +`dokkaGenerate` is the documentation gate: a public declaration without KDoc +fails it. + The backend checks in Gradle dependency locks and SHA-256 verification metadata to keep transitive versions and downloaded artifacts stable. When intentionally updating dependencies, run these commands from `backend/`, review both generated @@ -51,9 +59,13 @@ files, then rerun the ordinary backend gates: ```text gradle dependencies --write-locks --no-daemon -gradle test jacocoTestReport bootJar --write-verification-metadata sha256 --no-daemon +gradle --refresh-dependencies --write-verification-metadata sha256 help test jacocoTestReport bootJar dokkaGenerate --no-daemon ``` +Keep `--refresh-dependencies`. Written from a warm local cache, the metadata +misses files that Gradle did not download again, and the build then fails +verification on a clean machine while it passes locally. + Do not accept a checksum-only change without confirming the artifact version and source repository; the checksum is an integrity pin, not a trust review. @@ -61,7 +73,7 @@ The frontend tests assert canonical metadata, sitemap membership, asset shape, forum non-indexing, and the absence of retired site links. The Spring tests assert site-only API behavior, health, read-only operation, and missing account and registry routes. These gates are necessary but not a substitute for a -browser review of both themes, both languages, and narrow screens. +browser review of both themes, all four languages, and narrow screens. ## Asset checks @@ -96,14 +108,18 @@ link with a different brand domain. ## Forum placeholder deployment The forum is deliberately a static document. It has no form, identity flow, -post model, database, or discussion endpoint. Its language and theme controls -operate entirely in the browser. The page and its server response both carry +post model, database, or discussion endpoint. It shows its notice in the +language of the browser and has a theme button; both work entirely in the +browser. The page and its server response both carry noindex signals. The Nginx policy only allows same-origin CSS, JavaScript, and images; it does not need `unsafe-inline`. Files under `forum/` are deployed as a unit. Deploying `index.html` without its version-matched `site.css`, `site.js`, and two SVG files can leave a -half-styled page or controls that no longer work. The forum Nginx config +half-styled page or controls that no longer work. `index.html` refers to the +stylesheet and the script with a `?v=` value; change that value whenever either +file changes, because a CDN in front of the host can keep serving the old file, +or a cached "not found", for hours. The forum Nginx config permits only those named static paths. After copying, restore SELinux labels, validate the Nginx configuration, then reload Nginx. Do not turn SELinux off to work around a labeling mistake. @@ -113,24 +129,65 @@ Check the public URL for HTTP 200 and for these response properties: - `X-Robots-Tag: noindex, nofollow`; - Content Security Policy with same-origin script and style sources; - `site.css`, `site.js`, and the leopard SVG returning HTTP 200; -- no login, registration, posting, or package UI; and -- English by default, with working German and light-mode controls. +- no login, registration, posting, or package UI; +- no language menu, and the notice in the language of the browser, English for + a browser whose language has no translation; and +- a working light-mode control. -The forum can be deployed while the main language domain stays in +The forum can be deployed while the main language domain is in maintenance. Opening the real forum later is a separate product decision. -## Main-site rollout gate +## Releasing the main site -The current main-domain server uses `ops/nginx/xsharp-maintenance.conf`. -The future site config is `ops/nginx/xsharp.conf`. Merely committing the -future config, building the Vue bundle, or deploying the forum does **not** -authorize replacing the maintenance config. Keep the two configurations -mutually exclusive in the live Nginx include directory. +On the server the site lives in one directory: -Before opening the main site: +```text +/srv/xsharp/website/ + current/ + frontend/ contents of frontend/dist + forum/ the five forum files + backend/ the Spring jar, next to the files of the legacy process + previous-<timestamp>/ the release before, kept for rollback +``` -1. Review the English and German copy against the public language Spec and - current compiler behavior. +`current` is a real directory, not a symbolic link. A release replaces it by +renaming: + +1. Build the frontend and the jar from the commit being released, and run the + local gates. +2. On the server, copy `current` to a new directory next to it with + `cp -a`, which keeps owners, modes and SELinux labels, and replace the + changed parts inside the copy. +3. Start the new jar on a free loopback port as the service user, request + `/health` and `/api/v1/site`, and stop it. +4. Rename `current` to `previous-<timestamp>` and the new directory to + `current`. +5. Run `restorecon -R` on `current/frontend` and `current/forum`. +6. Restart the API service, then check the public routes from outside. + +Step 5 has to come after step 4. The SELinux rules that let Nginx read the +files are bound to the paths below `current`. Run on a staging path, +`restorecon` gives the files the default label of that path, and Nginx then +answers 403 or 500 for every file. Do not switch SELinux to permissive to get +around a label. + +A CDN in front of the host caches static files, `robots.txt` included, for +hours. After a release, a changed file at an unchanged URL can look stale from +outside while the server already has the new one; compare with a request made +on the server itself before concluding that a release failed. + +## Maintenance mode + +`ops/nginx/xsharp-maintenance.conf` answers the main domain with 503 and keeps +the legacy redirect working. `ops/nginx/xsharp.conf` serves the site. Keep the +two mutually exclusive in the live Nginx include directory. Building the Vue +bundle or deploying the forum does not by itself authorize switching between +them. + +Before switching the main site from maintenance to public: + +1. Review the copy in every published language against the public language + Spec and current compiler behavior. 2. Confirm the homepage actually answers why the language exists and does not describe planned compiler work as a finished feature. 3. Verify the three route bodies, both themes, keyboard focus, and responsive @@ -160,15 +217,20 @@ true while `xsharp-lang.xyz` itself returns 503. The redirect certificate uses the existing SAN entry for the old hostname. Do not remove the redirect in the course of cleaning up language-site copy. -The redirect remains in the future Nginx config as well. It is routing +The redirect is part of the site config as well. It is routing compatibility, not a ViGet API or a user-facing section of the language site. ## Rollback -Keep the prior Nginx configuration and the last known good frontend artifact -available until post-rollout checks pass. A rollback returns the main language -vhost to maintenance, checks `nginx -t`, reloads Nginx, and confirms the -expected 503. The forum and legacy redirect should remain unaffected. +Keep the previous release directory until the checks after a release pass. A +rollback of a release renames `current` away and `previous-<timestamp>` back to +`current`, runs `restorecon -R` on `current/frontend` and `current/forum`, and +restarts the API service. + +Keep the prior Nginx configuration as well. Taking the main site offline +returns its vhost to maintenance, checks `nginx -t`, reloads Nginx, and +confirms the expected 503. The forum and legacy redirect should remain +unaffected. Do not roll back by deleting certificates, disabling SELinux, or pointing unrelated hostnames at the language frontend. If the Spring process fails, @@ -178,27 +240,30 @@ boundary is clear. ## Monitoring and privacy -Expected main-domain maintenance responses are 503 with `Retry-After`. -Expected forum responses are 200 for the five static files and 404 for other -paths. Expected legacy redirect responses are 308. After the public launch, -the main language routes should be 200 and `/api/v1/site` should return only -the website's non-sensitive state. A 200 from a retired login or registry -route is a regression. +The main language routes answer 200, and `/api/v1/site` returns only the +website's non-sensitive state. Expected forum responses are 200 for the five +static files and 404 for other paths. Expected legacy redirect responses are +308. In maintenance mode the main domain answers 503 with `Retry-After`. A 200 +from a retired login or registry route is a regression. Neither the static site nor the forum placeholder should send personal data -to a third-party analytics service. Theme and language preferences are kept +to a third-party analytics service. The content security policy allows +same-origin scripts only, so a script added by an intermediary is blocked by +the browser rather than run. Theme and language preferences are kept locally in the browser. Nginx and Spring logs still need normal retention and access-control practices, but this repository does not define an account database or a user profile store. ## Post-release review -After a public rollout, perform the same checks from a clean browser profile -with no saved language or theme choice. Confirm that the default is dark and -English. Switch to German, navigate through both documentation routes, and -confirm the choice persists. Switch to light mode and repeat. The forum has -its own local preference keys and should not unexpectedly change the main -site's settings. +After a release, perform the same checks from a clean browser profile with no +saved language or theme choice. Confirm that the theme is dark and that the +language is the browser's when it is one of the four, and English otherwise. +Choose another language in the menu, navigate through both documentation +routes, and confirm the choice persists. Choose Hebrew and confirm that text +runs right to left while the layout and the code blocks stay as they are. +Switch to light mode and repeat. The forum keeps its own theme key and follows +the browser language; it should not change the main site's settings. Inspect the page source as well as the rendered page. The initial HTML has English home metadata; JavaScript updates metadata after route and locale diff --git a/Documents/README.md b/Documents/README.md new file mode 100644 index 0000000..6bdfa8a --- /dev/null +++ b/Documents/README.md @@ -0,0 +1,16 @@ +<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> --> +<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 --> + +# Website documentation + +These documents describe the Visual X# language website as it is built and published today. + +| Document | Read it when you want to know | +| --- | --- | +| [Architecture](ARCHITECTURE.md) | the three parts of the repository, what serves which host, and what the site deliberately does not contain | +| [Localization](LOCALIZATION.md) | which languages are published, how a page picks one, how Hebrew is laid out, and how to add a language | +| [Development](DEVELOPMENT.md) | how to run and check the frontend, the backend and the forum page, and what CI enforces | +| [Content contract](CONTENT.md) | what the pages may claim about the language, and the rules for copy and translations | +| [Operations](OPERATIONS.md) | routes, release steps, rollback and the checks after a release | + +The [repository README](../README.md) is the short introduction; start there if you are new. @@ -9,7 +9,7 @@ This repository owns the public language website at [xsharp-lang.xyz](https://xs Progmasoft's corporate or account sites. Visual X# uses its own supplied purple leopard SVG identity; the Progmasoft mark is a different brand. -## Routes and rollout +## Routes - `/` answers why Visual X# exists and gives an honest implementation-status caveat. - `/docs/overview` explains the design direction. @@ -17,16 +17,27 @@ the Progmasoft mark is a different brand. - `forum.xsharp-lang.xyz` is a standalone informational page. It is *not* a forum service; accounts and posts do not exist there yet. It stays noindex until a real community forum is launched. -The language domain remains on its maintenance page during the redesign. Do not replace the maintenance Nginx config with -`ops/nginx/xsharp.conf` merely because the new frontend builds. Publishing the site requires content review, route checks, -and an explicit rollout decision. The forum placeholder can be deployed independently. See the -[operations runbook](Documents/OPERATIONS.md) for the exact route and rollout boundaries. +The site is public and served with `ops/nginx/xsharp.conf`. `ops/nginx/xsharp-maintenance.conf` remains in the +repository as the configuration to fall back to when the main domain has to be taken offline. The forum page is +deployed independently of the main site. See the [operations runbook](Documents/OPERATIONS.md) for the route and +release boundaries. + +## Documentation + +- [Architecture](Documents/ARCHITECTURE.md): the three parts of the repository and what each serves. +- [Localization](Documents/LOCALIZATION.md): language selection, text direction and adding a language. +- [Development](Documents/DEVELOPMENT.md): local setup, tests and what CI enforces. +- [Content contract](Documents/CONTENT.md): what the pages may claim and the rules for copy. +- [Operations](Documents/OPERATIONS.md): routes, release steps and rollback. ## Frontend -The frontend is Vue 3 + Vite + TypeScript. It defaults to dark mode and `en-US`, with `de-DE` and `ru-RU` options and a light theme. -Preferences stay in local sto |