Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLeitwolf112026-11-10 03:15:05 +0300
committerLeitwolf112026-11-10 03:15:05 +0300
commit5b1cb02c51b0b9cc65371d6f557343292aeb2326 (patch)
tree0d74d23266dcd3def8c191e0721239c38731da88
parentcc6ca15df51dea156cac1ea697fb620e9b6f08ca (diff)
downloadwebsite-ops/tala-run-unavailable.tar.gz
Close tala.run: every page leads to the maintenance noticeops/tala-run-unavailable
The Tala language and its tools are no longer developed as of 2026-11-10; Progmasoft puts its work into Tazel, now a build system for C, C++, Objective-C, Objective-C++ and Zig. tala.run is prepared for OpenTala, a kernel, and TalaOS. - ops/nginx/tala.conf and forum.conf: every page of tala.run, www.tala.run, talac.tala.run, talafmt.tala.run, talalint.tala.run and forum.tala.run answers 302 to https://tala.run/unavailable?reason=maintenance. 302, not 308, so neither browsers nor Cloudflare keep the redirect once the domain serves other pages. - /unavailable answers 503 with Retry-After, X-Robots-Tag noindex and a strict policy; its body is ops/maintenance/unavailable.html, a static page in English, German, Russian and Hebrew that names OpenTala and TalaOS. - ACME on port 80, tazel.tala.run (to tazel.net), the former domain's redirects and viget.xsharp-lang.xyz (to viget.progmasoft.com) keep working. ops/nginx/tala-maintenance.conf, the 503 vhost it replaces, is removed. - README and Documents/OPERATIONS.md say so; the site tests check the redirects, the notice and its four languages instead of the served routes. Installed on the first server before this PR (previous files in /root/tala-run-before-unavailable); every host checked from outside.
-rw-r--r--Documents/OPERATIONS.md52
-rw-r--r--README.md12
-rw-r--r--frontend/tests/site-integrity.test.mjs117
-rw-r--r--ops/maintenance/unavailable.html89
-rw-r--r--ops/nginx/forum.conf14
-rw-r--r--ops/nginx/tala-maintenance.conf72
-rw-r--r--ops/nginx/tala.conf274
7 files changed, 202 insertions, 428 deletions
diff --git a/Documents/OPERATIONS.md b/Documents/OPERATIONS.md
index cf4db71..1a17a90 100644
--- a/Documents/OPERATIONS.md
+++ b/Documents/OPERATIONS.md
@@ -26,9 +26,10 @@ compatibility, but it does not make package functions part of this website.
tala.run has no ViGet host; the registry lives at `viget.progmasoft.com`.
-The sites are served with `ops/nginx/tala.conf`. While it is switched to
-`ops/nginx/tala-maintenance.conf`, the routes of the three sites answer 503 and
-`/api/v1/site` is not exposed. One certificate, `/etc/letsencrypt/live/tala.run/`,
+The sites are closed (2026-11-10): `ops/nginx/tala.conf` and `ops/nginx/forum.conf` answer every page of
+tala.run, www.tala.run, talac.tala.run, talafmt.tala.run, talalint.tala.run and forum.tala.run with a 302 to
+`https://tala.run/unavailable?reason=maintenance`, which answers 503 with `ops/maintenance/unavailable.html`
+(see Maintenance mode). `/api/v1/site` is not exposed. One certificate, `/etc/letsencrypt/live/tala.run/`,
covers tala.run, www.tala.run, talac.tala.run, tazel.tala.run and forum.tala.run;
the former domain keeps its own certificates for its redirects.
@@ -179,35 +180,22 @@ on the server itself before concluding that a release failed.
## Maintenance mode
-`ops/nginx/tala-maintenance.conf` answers the three sites with 503 and keeps
-the legacy redirect working. `ops/nginx/tala.conf` serves them. 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
- widths in a real browser.
-4. Confirm all GitHub destinations and the forum link respond correctly.
-5. Run both build/test sets shown above.
-6. Install the Vue `dist/` contents at the static root named in the Nginx
- config, preserving file permissions and SELinux labels.
-7. Test the published API on loopback with the shared ASP.NET Core 10 runtime
- before replacing the running API service.
-8. Confirm the old service's unrelated status function has moved to the
- correct owner before retiring the old process.
-9. Make one explicit Nginx configuration switch, run `nginx -t`, and reload.
-10. Check the public routes and headers again from outside the server.
-
-The API service is not required for the static homepage render. If it is
-not ready, do not pretend it is ready by rewriting the API path. Delay the
-rollout or explicitly publish static pages without the API after reviewing
-the impact of that decision.
+tala.run is closed while the domain is prepared for OpenTala, a kernel, and TalaOS; the Tala language and its tools
+are no longer developed (2026-11-10).
+
+- Every page of tala.run, www.tala.run, talac.tala.run, talafmt.tala.run, talalint.tala.run and forum.tala.run
+ answers `302` to `https://tala.run/unavailable?reason=maintenance`. The redirect is temporary on purpose, so that
+ neither browsers nor Cloudflare keep it once the domain serves other pages.
+- `https://tala.run/unavailable` answers `503` with `Retry-After`, `noindex` and a strict policy, its body being
+ `ops/maintenance/unavailable.html`, installed as `/usr/share/nginx/html/maintenance/tala-unavailable.html`
+ (`restorecon` after copying).
+- ACME challenges on port 80 still answer, so the `tala.run` certificate renews.
+- `tazel.tala.run` keeps leading to tazel.net, and the former domain keeps redirecting to tala.run, from where it
+ reaches the notice.
+
+The frontend release under `/srv/xsharp/website/current` and the `tala-site-api` service stay on the server but are
+not reachable. Opening a site on the domain again replaces `ops/nginx/tala.conf` and `ops/nginx/forum.conf`; run
+`nginx -t`, reload, and check every host from outside.
## Legacy redirect during maintenance
diff --git a/README.md b/README.md
index acddd64..cdc994c 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Gr
# Tala website
+> [!IMPORTANT]
+> The Tala language and its tools (Talac, talafmt, talalint, tala-analyzer) are no longer developed as of 2026-11-10.
+> Progmasoft puts its work into [Tazel](https://tazel.net), now a build system for C, C++, Objective-C,
+> Objective-C++ and Zig. tala.run is closed for maintenance: every page and subdomain leads to
+> `https://tala.run/unavailable?reason=maintenance` while the domain is prepared for OpenTala, a kernel, and TalaOS.
+> The site below is kept for reference.
+
This repository owns the public language website at [tala.run](https://tala.run) and the documentation of the
compiler at [talac.tala.run](https://talac.tala.run). The build system has its own site, [tazel.net](https://tazel.net).
It does not own
@@ -27,9 +34,8 @@ One frontend build serves three sites and picks its pages from the host name:
Every path of the former domain, `xsharp-lang.xyz`, redirects permanently to the same path of `tala.run`. tala.run has
no ViGet host; the registry lives at `viget.progmasoft.com`.
-The sites are public and served with `ops/nginx/tala.conf`. `ops/nginx/tala-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
+The sites are closed: `ops/nginx/tala.conf` and `ops/nginx/forum.conf` send every page to
+`https://tala.run/unavailable?reason=maintenance`, a page of its own (`ops/maintenance/unavailable.html`). See the [operations runbook](Documents/OPERATIONS.md) for the route and
release boundaries.
## Documentation
diff --git a/frontend/tests/site-integrity.test.mjs b/frontend/tests/site-integrity.test.mjs
index ba131f0..68fedf1 100644
--- a/frontend/tests/site-integrity.test.mjs
+++ b/frontend/tests/site-integrity.test.mjs
@@ -62,13 +62,9 @@ test("every site publishes exactly its own pages in its sitemap", () => {
test("the forum placeholder is explicitly non-indexable and not an account service", () => {
const forum = readFileSync(join(websiteRoot, "forum/index.html"), "utf8");
- const nginx = readFileSync(join(websiteRoot, "ops/nginx/forum.conf"), "utf8");
assert.match(forum, /name="robots" content="noindex, nofollow"/);
assert.match(forum, /There are no accounts,/);
assert.match(forum, /posts, or discussion service/);
- assert.match(nginx, /X-Robots-Tag "noindex, nofollow"/);
- assert.match(nginx, /script-src 'self'; style-src 'self'/);
- assert.doesNotMatch(nginx, /unsafe-inline/);
assert.match(forum, /src="\/site\.js[?"]/);
assert.match(forum, /href="\/site\.css[?"]/);
const normalizedForum = forum.toLowerCase();
@@ -155,52 +151,60 @@ test("the site examples are copied from the compiler repository when available",
assert.ok(guide.includes(program("HelloWorld")), "the guide diverged from the compiler example");
});
-test("maintenance mode preserves the legacy redirect without opening the language site", () => {
- const maintenance = readFileSync(join(websiteRoot, "ops/nginx/tala-maintenance.conf"), "utf8");
- const live = readFileSync(join(websiteRoot, "ops/nginx/tala.conf"), "utf8");
+test("every page of tala.run and its subdomains leads to the notice", () => {
+ const live = readFileSync(join(websiteRoot, "ops/nginx/tala.conf"), "utf8").replaceAll(
+ "\r\n",
+ "\n",
+ );
+ const forum = readFileSync(join(websiteRoot, "ops/nginx/forum.conf"), "utf8").replaceAll(
+ "\r\n",
+ "\n",
+ );
+ const notice = "https://tala.run/unavailable?reason=maintenance";
+ const target = notice.replaceAll(".", "\\.").replaceAll("?", "\\?").replaceAll("/", "\\/");
+ // tala.run answers the notice itself with 503 and sends every other path to it, temporarily.
+ assert.match(live, /server_name tala\.run;[^]*?location = \/unavailable \{\s*return 503;/);
+ assert.match(live, /error_page 503 \/tala-unavailable\.html;/);
+ assert.match(live, /alias \/usr\/share\/nginx\/html\/maintenance\/tala-unavailable\.html;/);
+ assert.match(live, /add_header Retry-After "\d+" always;/);
+ assert.match(live, /add_header X-Robots-Tag "noindex, nofollow" always;/);
+ assert.match(live, new RegExp(`location / \\{\\s*return 302 ${target};`));
assert.match(
- maintenance,
- /server_name tala\.run www\.tala\.run talac\.tala\.run tazel\.tala\.run;/,
+ live,
+ new RegExp(
+ `server_name www\\.tala\\.run talac\\.tala\\.run talafmt\\.tala\\.run talalint\\.tala\\.run;[^}]*return 302 ${target};`,
+ ),
+ );
+ assert.match(
+ forum,
+ new RegExp(
+ `server_name forum\\.tala\\.run;\\n server_tokens off;[^}]*return 302 ${target};`,
+ ),
+ );
+ assert.doesNotMatch(live + forum, /return 30[178] https:\/\/tala\.run\/unavailable/);
+ // The pages of the closed site are no longer served from these hosts.
+ assert.doesNotMatch(live + forum, /root \/srv\/xsharp\/website\/current/);
+ // The registry's former address keeps its redirect.
+ assert.match(
+ live,
+ /server_name viget\.xsharp-lang\.xyz;[^}]*return 308 https:\/\/viget\.progmasoft\.com\$request_uri;/,
+ );
+ // ACME still answers on port 80, so the certificate renews.
+ assert.match(
+ live,
+ /location \^~ \/\.well-known\/acme-challenge\/ \{\s*root \/var\/lib\/letsencrypt;/,
);
- assert.match(maintenance, /location \/ \{ return 503; \}/);
- assert.match(maintenance, /server_name viget\.xsharp-lang\.xyz/);
- assert.match(maintenance, /return 308 https:\/\/viget\.progmasoft\.com\$request_uri/);
- assert.match(live, /server_name viget\.xsharp-lang\.xyz/);
- assert.match(live, /location = \/api\/v1\/site/);
- assert.match(live, /location \^~ \/api\/ \{\s*return 404;/);
});
-test("only the routes of the application get the app shell; other paths answer 404", () => {
- const live = readFileSync(join(websiteRoot, "ops/nginx/tala.conf"), "utf8");
- assert.doesNotMatch(live, /try_files \$uri \$uri\/ \/index\.html;/);
- assert.equal(live.match(/try_files \$uri \$uri\/ @application;/g)?.length, 4);
- assert.equal(live.match(/error_page 404 \/index\.html;/g)?.length, 4);
- const patterns = [...live.matchAll(/if \(\$uri !~ "([^"]+)"\)/g)].map((match) => match[1]);
- assert.equal(new Set(patterns).size, 1, "every site must accept the same routes");
- const route = new RegExp(patterns[0]);
- const pages = [
- "public/sitemap.xml",
- "public/hosts/talac/sitemap.xml",
- "public/hosts/talafmt/sitemap.xml",
- "public/hosts/talalint/sitemap.xml",
- ]
- .flatMap(locations)
- .map((url) => new URL(url).pathname);
- assert.ok(pages.length > 3);
- for (const path of pages) {
- assert.match(path, route, `${path} is in a sitemap but would answer 404`);
- }
- for (const path of [
- "/wp-login.php",
- "/wp-admin/install.php",
- "/docs",
- "/docs/",
- "/docs/a/b",
- "/.env",
- "/a",
- ]) {
- assert.doesNotMatch(path, route, `${path} would be served as a page`);
+test("the notice is a page of its own that names what comes to tala.run", () => {
+ const page = readFileSync(join(websiteRoot, "ops/maintenance/unavailable.html"), "utf8");
+ assert.match(page, /<meta name="robots" content="noindex, nofollow" \/>/);
+ assert.doesNotMatch(page, /<script\b/);
+ for (const language of ["en", "de", "ru", "he"]) {
+ assert.match(page, new RegExp(`<section lang="${language}"`));
}
+ assert.equal(page.split("OpenTala").length - 1, 4, "every language names OpenTala");
+ assert.equal(page.split("TalaOS").length - 1, 4, "every language names TalaOS");
});
test("the former domain lands on the same path of tala.run and tala.run has no ViGet host", () => {
@@ -211,18 +215,10 @@ test("the former domain lands on the same path of tala.run and tala.run has no V
/server_name xsharp-lang\.xyz www\.xsharp-lang\.xyz docs\.xsharp-lang\.xyz spec\.xsharp-lang\.xyz;[^}]*return 308 https:\/\/tala\.run\$request_uri;/s,
);
assert.match(
- live,
- /server_name www\.tala\.run;[^}]*return 308 https:\/\/tala\.run\$request_uri;/s,
- );
- assert.match(
forum,
/server_name forum\.xsharp-lang\.xyz;[^}]*return 308 https:\/\/forum\.tala\.run\$request_uri;/s,
);
assert.doesNotMatch(live + forum, /viget\.tala\.run/);
- for (const host of ["talac", "talafmt", "talalint"]) {
- assert.match(live, new RegExp(`try_files /hosts/${host}/robots\\.txt =404;`));
- assert.match(live, new RegExp(`try_files /hosts/${host}/sitemap\\.xml =404;`));
- }
});
test("tazel.tala.run only redirects to tazel.net, its two pages to their own", () => {
@@ -303,17 +299,8 @@ test("the production API binds to loopback and matches its Nginx proxy port", ()
assert.ok(service.includes("Environment=PORT=5086"));
assert.ok(service.includes("ExecStart=/usr/bin/dotnet "));
assert.doesNotMatch(service, /java/i);
- assert.ok(nginx.includes("proxy_pass http://127.0.0.1:5086/api/v1/site;"));
-});
-
-test("the forum assets are allowed by its restrictive Nginx policy", () => {
- const config = readFileSync(join(websiteRoot, "ops/nginx/forum.conf"), "utf8");
- const forum = readFileSync(join(websiteRoot, "forum/index.html"), "utf8");
- for (const filename of ["site.css", "site.js", "tala-mark.svg"]) {
- assert.ok(existsSync(join(websiteRoot, "forum", filename)), `${filename} missing`);
- assert.ok(config.includes(`location = /${filename}`), `${filename} blocked by Nginx`);
- assert.ok(forum.includes(`/${filename}`), `${filename} not referenced by forum`);
- }
+ // tala.run is closed, so Nginx does not expose the API.
+ assert.doesNotMatch(nginx, /proxy_pass/);
});
test("navigation, router, and sitemap agree on the public language pages", () => {
@@ -376,7 +363,7 @@ test("the former domain is left to the server: the page carries no notice about
});
test("every tala.run host behaves as if HSTS were preloaded for its top-level domain", () => {
- for (const file of ["tala.conf", "forum.conf", "tala-maintenance.conf"]) {
+ for (const file of ["tala.conf", "forum.conf"]) {
const config = readFileSync(join(websiteRoot, "ops/nginx", file), "utf8");
const blocks = config.split(/\nserver \{/).slice(1);
for (const block of blocks) {
diff --git a/ops/maintenance/unavailable.html b/ops/maintenance/unavailable.html
new file mode 100644
index 0000000..35c138a
--- /dev/null
+++ b/ops/maintenance/unavailable.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<!-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com> -->
+<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 -->
+<!-- Installed as /usr/share/nginx/html/maintenance/tala-unavailable.html; see ops/nginx/tala.conf. -->
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="robots" content="noindex, nofollow" />
+ <title>tala.run is unavailable</title>
+ <style>
+ :root {
+ color-scheme: light dark;
+ --background: #f6f8f9;
+ --surface: #ffffff;
+ --ink: #13222a;
+ --muted: #4d5f68;
+ --line: #d9e1e5;
+ }
+ @media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0c171d;
+ --surface: #13222a;
+ --ink: #e6eef1;
+ --muted: #9db0b8;
+ --line: #24363f;
+ }
+ }
+ * {
+ box-sizing: border-box;
+ }
+ body {
+ margin: 0;
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+ padding: 24px 16px;
+ background: var(--background);
+ color: var(--ink);
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+ line-height: 1.55;
+ }
+ main {
+ width: 100%;
+ max-width: 640px;
+ display: grid;
+ gap: 16px;
+ }
+ section {
+ padding: 20px 24px;
+ border: 1px solid var(--line);
+ border-radius: 12px;
+ background: var(--surface);
+ }
+ h1 {
+ margin: 0 0 6px;
+ font-size: 1.25rem;
+ }
+ p {
+ margin: 0;
+ color: var(--muted);
+ }
+ a {
+ color: inherit;
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <section lang="en">
+ <h1>tala.run is unavailable</h1>
+ <p>The site is closed for maintenance. It is being prepared for OpenTala, a kernel, and TalaOS; its pages lead here until then.</p>
+ </section>
+ <section lang="de">
+ <h1>tala.run ist nicht erreichbar</h1>
+ <p>Die Website ist wegen Wartung geschlossen. Sie wird für OpenTala, einen Kernel, und TalaOS vorbereitet; bis dahin führen ihre Seiten hierher.</p>
+ </section>
+ <section lang="ru">
+ <h1>tala.run недоступен</h1>
+ <p>Сайт закрыт на обслуживание. Он готовится для OpenTala — ядра — и TalaOS; до тех пор его страницы ведут сюда.</p>
+ </section>
+ <section lang="he" dir="rtl">
+ <h1>‏tala.run אינו זמין</h1>
+ <p>האתר סגור לתחזוקה. הוא מוכן עבור OpenTala, ליבת מערכת הפעלה, ועבור TalaOS; עד אז הדפים שלו מובילים לכאן.</p>
+ </section>
+ <p><a href="https://progmasoft.com/">Progmasoft</a></p>
+ </main>
+ </body>
+</html>
diff --git a/ops/nginx/forum.conf b/ops/nginx/forum.conf
index 5ae383c..bae046b 100644
--- a/ops/nginx/forum.conf
+++ b/ops/nginx/forum.conf
@@ -19,8 +19,6 @@ server {
listen [::]:443 ssl;
http2 on;
server_name forum.tala.run;
- root /srv/xsharp/website/current/forum;
- index index.html;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
@@ -28,18 +26,10 @@ server {
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
- add_header X-Robots-Tag "noindex, nofollow" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- add_header Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'" always;
- location = / { try_files /index.html =404; }
- location = /tala.svg { try_files $uri =404; }
- location = /tala-mark.svg { try_files $uri =404; }
- location = /site.css { try_files $uri =404; }
- location = /site.js { try_files $uri =404; }
- location / { return 404; }
+ # tala.run is closed; see tala.conf.
+ return 302 https://tala.run/unavailable?reason=maintenance;
}
# The former address of the same page.
diff --git a/ops/nginx/tala-maintenance.conf b/ops/nginx/tala-maintenance.conf
deleted file mode 100644
index 35e78d9..0000000
--- a/ops/nginx/tala-maintenance.conf
+++ /dev/null
@@ -1,72 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1
-
-# The language pages stay closed while this file is active. Keep the retired
-# ViGet hostname working as a redirect; it is not a website API or page.
-server {
- listen 80;
- listen [::]:80;
- server_name tala.run www.tala.run talac.tala.run tazel.tala.run;
-
- location ^~ /.well-known/acme-challenge/ {
- root /var/lib/letsencrypt;
- default_type text/plain;
- }
- location / { return 308 https://$host$request_uri; }
-}
-
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name tala.run www.tala.run talac.tala.run tazel.tala.run;
- server_tokens off;
-
- ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/tala.run/privkey.pem;
- include /etc/letsencrypt/options-ssl-nginx.conf;
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-
- add_header Retry-After "3600" always;
- add_header Cache-Control "no-store" always;
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "DENY" always;
- add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'" always;
-
- error_page 503 /maintenance.html;
- location = /maintenance.html {
- internal;
- alias /usr/share/nginx/html/maintenance/xsharp.html;
- }
- location / { return 503; }
-}
-
-server {
- listen 80;
- listen [::]:80;
- server_name viget.xsharp-lang.xyz;
- server_tokens off;
-
- location ^~ /.well-known/acme-challenge/ {
- root /var/lib/letsencrypt;
- default_type text/plain;
- }
- location / { return 308 https://viget.progmasoft.com$request_uri; }
-}
-
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name viget.xsharp-lang.xyz;
- server_tokens off;
-
- ssl_certificate /etc/letsencrypt/live/xsharp-lang.xyz/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/xsharp-lang.xyz/privkey.pem;
- include /etc/letsencrypt/options-ssl-nginx.conf;
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-
- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
- return 308 https://viget.progmasoft.com$request_uri;
-}
diff --git a/ops/nginx/tala.conf b/ops/nginx/tala.conf
index a8164ff..f7aa02a 100644
--- a/ops/nginx/tala.conf
+++ b/ops/nginx/tala.conf
@@ -1,13 +1,15 @@
# SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
# SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1
-# One frontend build serves four sites: tala.run for the language,
-# talac.tala.run for the compiler, talafmt.tala.run for the formatter and
-# talalint.tala.run for the linter. The page picks its content from the host
-# name. xsharp-lang.xyz, the former domain, answers with a permanent redirect to
-# the same path on tala.run. tazel.tala.run, where the build system was
-# documented, answers with a permanent redirect to tazel.net, its own site.
-# The policies allow Cloudflare Web Analytics, which Cloudflare adds to each page.
+# tala.run is closed. Every page of tala.run, www.tala.run, talac.tala.run,
+# talafmt.tala.run and talalint.tala.run (and forum.tala.run, in forum.conf)
+# leads with a temporary redirect to https://tala.run/unavailable?reason=maintenance,
+# which answers 503 with a page of its own. The redirect is 302, not 308: the
+# domain will serve other pages later, so neither browsers nor Cloudflare may
+# keep it. ACME challenges on port 80 still work, so the certificates renew.
+# xsharp-lang.xyz, the former domain, still redirects to tala.run, and from
+# there to the notice. tazel.tala.run keeps leading to tazel.net, the site of
+# Tazel, which goes on.
server {
listen 80 default_server;
@@ -48,146 +50,6 @@ server {
listen [::]:443 ssl;
http2 on;
server_name tala.run;
-
- root /srv/xsharp/website/current/frontend;
- index index.html;
- server_tokens off;
-
- ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/tala.run/privkey.pem;
- include /etc/letsencrypt/options-ssl-nginx.conf;
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-
- add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; connect-src 'self' https://cloudflareinsights.com; img-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "DENY" always;
-
- # Keep retired registry paths closed on the language host. Accounts and
- # packages belong to separate Progmasoft services, not this Vue artifact.
- location = /repo {
- return 404;
- }
-
- location ^~ /repo/ {
- return 404;
- }
-
- # Only the language site's own public metadata crosses into the API.
- # A catch-all /api proxy would accidentally expose legacy registry routes.
- location = /api/v1/site {
- proxy_pass http://127.0.0.1:5086/api/v1/site;
- proxy_set_header Host $host;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_connect_timeout 2s;
- proxy_read_timeout 5s;
- }
-
- location ^~ /api/ {
- return 404;
- }
-
- # The robots and sitemap files of the two documentation hosts.
- location ^~ /hosts/ {
- return 404;
- }
-
- location / {
- # Vue Router uses history URLs; real files still win before the app shell.
- try_files $uri $uri/ @application;
- }
-
- # The application has two kinds of routes: the landing page and
- # /docs/<page>. Any other path that is not a file is answered with 404, so
- # that unknown addresses, including the probes for /wp-login.php and
- # friends, are not served as pages. The body of the 404 is still the
- # application, whose router takes a visitor to the landing page.
- location @application {
- if ($uri !~ "^/(docs/[a-z0-9-]+/?)?$") {
- return 404;
- }
- rewrite ^ /index.html break;
- }
-
- error_page 404 /index.html;
-
- location ~ /\. {
- deny all;
- }
-}
-
-# talac.tala.run, talafmt.tala.run and talalint.tala.run serve
-# the same build; each answers for robots.txt and sitemap.xml with its own file.
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name talac.tala.run;
-
- root /srv/xsharp/website/current/frontend;
- index index.html;
- server_tokens off;
-
- ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/tala.run/privkey.pem;
- include /etc/letsencrypt/options-ssl-nginx.conf;
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-
- add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; connect-src 'self' https://cloudflareinsights.com; img-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "DENY" always;
-
- location = /robots.txt {
- try_files /hosts/talac/robots.txt =404;
- }
-
- location = /sitemap.xml {
- try_files /hosts/talac/sitemap.xml =404;
- }
-
- location ^~ /hosts/ {
- return 404;
- }
-
- location ^~ /api/ {
- return 404;
- }
-
- location / {
- try_files $uri $uri/ @application;
- }
-
- # The application has two kinds of routes: the landing page and
- # /docs/<page>. Any other path that is not a file is answered with 404, so
- # that unknown addresses, including the probes for /wp-login.php and
- # friends, are not served as pages. The body of the 404 is still the
- # application, whose router takes a visitor to the landing page.
- location @application {
- if ($uri !~ "^/(docs/[a-z0-9-]+/?)?$") {
- return 404;
- }
- rewrite ^ /index.html break;
- }
-
- error_page 404 /index.html;
-
- location ~ /\. {
- deny all;
- }
-}
-
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name talafmt.tala.run;
-
- root /srv/xsharp/website/current/frontend;
- index index.html;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
@@ -195,48 +57,32 @@ server {
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
- add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; connect-src 'self' https://cloudflareinsights.com; img-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "DENY" always;
- location = /robots.txt {
- try_files /hosts/talafmt/robots.txt =404;
+ # The notice itself. 503 with Retry-After tells crawlers the closure is
+ # temporary and keeps the page out of their index.
+ location = /unavailable {
+ return 503;
}
- location = /sitemap.xml {
- try_files /hosts/talafmt/sitemap.xml =404;
- }
-
- location ^~ /hosts/ {
- return 404;
- }
-
- location ^~ /api/ {
- return 404;
+ error_page 503 /tala-unavailable.html;
+ location = /tala-unavailable.html {
+ internal;
+ alias /usr/share/nginx/html/maintenance/tala-unavailable.html;
+ default_type text/html;
+ add_header Retry-After "86400" always;
+ add_header Cache-Control "no-store" always;
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Frame-Options "DENY" always;
+ add_header X-Robots-Tag "noindex, nofollow" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" always;
}
location / {
- try_files $uri $uri/ @application;
- }
-
- # The application has two kinds of routes: the landing page and
- # /docs/<page>. Any other path that is not a file is answered with 404, so
- # that unknown addresses, including the probes for /wp-login.php and
- # friends, are not served as pages. The body of the 404 is still the
- # application, whose router takes a visitor to the landing page.
- location @application {
- if ($uri !~ "^/(docs/[a-z0-9-]+/?)?$") {
- return 404;
- }
- rewrite ^ /index.html break;
- }
-
- error_page 404 /index.html;
-
- location ~ /\. {
- deny all;
+ return 302 https://tala.run/unavailable?reason=maintenance;
}
}
@@ -244,10 +90,7 @@ server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
- server_name talalint.tala.run;
-
- root /srv/xsharp/website/current/frontend;
- index index.html;
+ server_name www.tala.run talac.tala.run talafmt.tala.run talalint.tala.run;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
@@ -255,49 +98,9 @@ server {
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
- add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; connect-src 'self' https://cloudflareinsights.com; img-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "DENY" always;
-
- location = /robots.txt {
- try_files /hosts/talalint/robots.txt =404;
- }
-
- location = /sitemap.xml {
- try_files /hosts/talalint/sitemap.xml =404;
- }
-
- location ^~ /hosts/ {
- return 404;
- }
-
- location ^~ /api/ {
- return 404;
- }
-
- location / {
- try_files $uri $uri/ @application;
- }
-
- # The application has two kinds of routes: the landing page and
- # /docs/<page>. Any other path that is not a file is answered with 404, so
- # that unknown addresses, including the probes for /wp-login.php and
- # friends, are not served as pages. The body of the 404 is still the
- # application, whose router takes a visitor to the landing page.
- location @application {
- if ($uri !~ "^/(docs/[a-z0-9-]+/?)?$") {
- return 404;
- }
- rewrite ^ /index.html break;
- }
-
- error_page 404 /index.html;
- location ~ /\. {
- deny all;
- }
+ return 302 https://tala.run/unavailable?reason=maintenance;
}
server {
@@ -329,22 +132,6 @@ server {
}
}
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name www.tala.run;
- server_tokens off;
-
- ssl_certificate /etc/letsencrypt/live/tala.run/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/tala.run/privkey.pem;
- include /etc/letsencrypt/options-ssl-nginx.conf;
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
-
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- return 308 https://tala.run$request_uri;
-}
-