<feed xmlns='http://www.w3.org/2005/Atom'>
<title>progmaweb/ops/systemd, branch main</title>
<subtitle>The site and account service of Progmasoft: a Next.js frontend and an ASP.NET Core API.</subtitle>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/'/>
<entry>
<title>Harden the systemd units of the API and the web frontend</title>
<updated>2026-10-23T00:21:38+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-23T00:21:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=11d91cba681e48afe0fcec6640d07b9100301f86'/>
<id>11d91cba681e48afe0fcec6640d07b9100301f86</id>
<content type='text'>
systemd-analyze security rated both services 7.2 MEDIUM. They now
carry the restrictions the Tala website API already runs with: no
capabilities (empty bounding and ambient sets), private /dev, no
access to the clock, hostname, kernel logs, modules, tunables or
control groups, only AF_UNIX/AF_INET/AF_INET6 sockets, no new
namespaces, no realtime scheduling, IPC removed on stop, and a 0077
umask. MemoryDenyWriteExecute stays off, since CoreCLR and V8 JIT
need writable executable mappings.

Deployed and verified on the server: systemd-analyze verify is clean,
both services restarted and rate 3.0 OK, the API health endpoint and
the frontend answer 200 on loopback, and progmasoft.com,
account.progmasoft.com and viget.progmasoft.com answer 200.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
systemd-analyze security rated both services 7.2 MEDIUM. They now
carry the restrictions the Tala website API already runs with: no
capabilities (empty bounding and ambient sets), private /dev, no
access to the clock, hostname, kernel logs, modules, tunables or
control groups, only AF_UNIX/AF_INET/AF_INET6 sockets, no new
namespaces, no realtime scheduling, IPC removed on stop, and a 0077
umask. MemoryDenyWriteExecute stays off, since CoreCLR and V8 JIT
need writable executable mappings.

Deployed and verified on the server: systemd-analyze verify is clean,
both services restarted and rate 3.0 OK, the API health endpoint and
the frontend answer 200 on loopback, and progmasoft.com,
account.progmasoft.com and viget.progmasoft.com answer 200.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Keep accounts and sessions in PostgreSQL</title>
<updated>2026-10-06T19:53:46+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-06T19:53:46+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=1c986ff99b3c8f349e4a63c0e922276ddf7dc836'/>
<id>1c986ff99b3c8f349e4a63c0e922276ddf7dc836</id>
<content type='text'>
The account API kept everything in the memory of its process, so every
restart and every deployment of the API deleted all accounts and signed
everyone out.

PostgresAccountStore implements IAccountStore on PostgreSQL through Npgsql.
The three uniqueness rules are unique constraints, so two requests that race
for one name, address or Google identity are decided by the database. A
refused account is an ordinary answer, not an exception, and the rules are
reported in the order the in-memory store reports them. A session refers to
its account, and a session of an account that is not stored is refused; the
in-memory store now refuses it as well.

The schema is a list of numbered steps. The service takes the missing ones
when it starts, in one transaction under an advisory lock, and does not
start when the database cannot be reached or is newer than the release.
/health answers 503 while the database does not answer.

ConnectionStrings:Accounts selects PostgreSQL. A production service that is
given no database does not start; keeping accounts in memory there has to
be asked for with Accounts:Store=Memory. Development and the tests keep the
in-memory store by default.

The store gains RemoveExpiredSessionsAsync, and a background service calls
it at the start and once an hour: a session whose token is never presented
again no longer keeps its row for ever.

PostgreSQL keeps a timestamp to the microsecond. The store cuts a moment to
that precision and returns what it stored, and the service uses the
returned account, so the creation time in the registration response equals
the one every later read returns.

Tests. AccountStoreContractTests states the rules once and runs them on
both stores. Further tests cover the schema steps applied concurrently and a
database that is too new, the choice of the store from the configuration,
the removal of expired sessions, and an account with its session surviving
a restart of the service over HTTP. The PostgreSQL tests read their
database from PROGMAWEB_TEST_POSTGRES and work in a schema of their own
each. Without the variable they are skipped; CI starts PostgreSQL 16 for
them, and a test fails the run when the variable is missing there.

Operations. ops/README.md describes the database: the Unix socket with peer
authentication, so that no password exists, the one-time preparation, and
rollback across a schema step. The service unit starts after PostgreSQL.
docs/LIMITATIONS.md no longer lists the in-memory store and now lists the
missing backup and the missing deletion of accounts.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The account API kept everything in the memory of its process, so every
restart and every deployment of the API deleted all accounts and signed
everyone out.

PostgresAccountStore implements IAccountStore on PostgreSQL through Npgsql.
The three uniqueness rules are unique constraints, so two requests that race
for one name, address or Google identity are decided by the database. A
refused account is an ordinary answer, not an exception, and the rules are
reported in the order the in-memory store reports them. A session refers to
its account, and a session of an account that is not stored is refused; the
in-memory store now refuses it as well.

The schema is a list of numbered steps. The service takes the missing ones
when it starts, in one transaction under an advisory lock, and does not
start when the database cannot be reached or is newer than the release.
/health answers 503 while the database does not answer.

ConnectionStrings:Accounts selects PostgreSQL. A production service that is
given no database does not start; keeping accounts in memory there has to
be asked for with Accounts:Store=Memory. Development and the tests keep the
in-memory store by default.

The store gains RemoveExpiredSessionsAsync, and a background service calls
it at the start and once an hour: a session whose token is never presented
again no longer keeps its row for ever.

PostgreSQL keeps a timestamp to the microsecond. The store cuts a moment to
that precision and returns what it stored, and the service uses the
returned account, so the creation time in the registration response equals
the one every later read returns.

Tests. AccountStoreContractTests states the rules once and runs them on
both stores. Further tests cover the schema steps applied concurrently and a
database that is too new, the choice of the store from the configuration,
the removal of expired sessions, and an account with its session surviving
a restart of the service over HTTP. The PostgreSQL tests read their
database from PROGMAWEB_TEST_POSTGRES and work in a schema of their own
each. Without the variable they are skipped; CI starts PostgreSQL 16 for
them, and a test fails the run when the variable is missing there.

Operations. ops/README.md describes the database: the Unix socket with peer
authentication, so that no password exists, the one-time preparation, and
rollback across a schema step. The service unit starts after PostgreSQL.
docs/LIMITATIONS.md no longer lists the in-memory store and now lists the
missing backup and the missing deletion of accounts.
</pre>
</div>
</content>
</entry>
<entry>
<title>Treat intentional Next.js shutdown as successful</title>
<updated>2026-09-07T22:35:54+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-09-07T22:35:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=96699b883c12e079451c9a754da185cf1e26b3fe'/>
<id>96699b883c12e079451c9a754da185cf1e26b3fe</id>
<content type='text'>
Teach systemd that the Node runtime's SIGTERM exit status is an expected graceful stop. This prevents routine atomic deployments from leaving a misleading failed result after the replacement service is already healthy.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Teach systemd that the Node runtime's SIGTERM exit status is an expected graceful stop. This prevents routine atomic deployments from leaving a misleading failed result after the replacement service is already healthy.
</pre>
</div>
</content>
</entry>
<entry>
<title>Make Progmaweb API activation SELinux-stable</title>
<updated>2026-09-07T22:29:57+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-09-07T22:29:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=41b6d47bd69e0545420612e1eebe9d01fb77be9e'/>
<id>41b6d47bd69e0545420612e1eebe9d01fb77be9e</id>
<content type='text'>
Invoke the framework-dependent API through the installed dotnet runtime instead of relying on a copied apphost inode retaining executable SELinux metadata. Document the production service contract so immutable releases activate consistently on the AlmaLinux host.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Invoke the framework-dependent API through the installed dotnet runtime instead of relying on a copied apphost inode retaining executable SELinux metadata. Document the production service contract so immutable releases activate consistently on the AlmaLinux host.
</pre>
</div>
</content>
</entry>
<entry>
<title>Harden Progmaweb identity, localization, and operations</title>
<updated>2026-09-07T18:31:05+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-09-07T18:31:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=171b443addac11e07559b6c4127e622341fe1961'/>
<id>171b443addac11e07559b6c4127e622341fe1961</id>
<content type='text'>
Adopt Patent Grant 1.1, make the apex host canonical while excluding account surfaces from indexing, move the Progmasoft mail boundary into its owning repository, migrate the .NET 10 tests to native MTP, and add persistent light/dark and en-US/de-DE preferences across corporate and account experiences.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Adopt Patent Grant 1.1, make the apex host canonical while excluding account surfaces from indexing, move the Progmasoft mail boundary into its owning repository, migrate the .NET 10 tests to native MTP, and add persistent light/dark and en-US/de-DE preferences across corporate and account experiences.
</pre>
</div>
</content>
</entry>
<entry>
<title>Add secure Google sign-in to Progmasoft Account. Use the server-side authorization-code flow with an exact account-domain callback, temporary hardened external cookies, verified provider email matching, canonical Account-name creation for new users, and no separate ViGet publisher identity. Add frontend entry points, provider failure guidance, locked Google authentication dependencies, service secret injection, and backend regression coverage without committing OAuth credentials.</title>
<updated>2026-08-30T18:45:01+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-08-30T18:45:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=76c4c1876a86dbca0adf86750362b3581d6fc69d'/>
<id>76c4c1876a86dbca0adf86750362b3581d6fc69d</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Establish Progmasoft copyright ownership across the corporate website and account service, license all project-owned sources under AGPL-3.0-or-later, add the shared defensive Progmasoft patent grant with SPDX and REUSE coverage, preserve source-availability obligations, and align notices, npm and .NET package metadata, frontend, API, tests, deployment configuration, and repository documentation</title>
<updated>2026-08-29T08:01:02+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-08-29T08:01:02+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=c8073a82bbc8d39d8a45334bdcd969d00a3c3f1c'/>
<id>c8073a82bbc8d39d8a45334bdcd969d00a3c3f1c</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Complete production activation hardening: allow the explicit loopback and canonical Progmasoft hosts used by the Next.js API proxy, and omit MemoryDenyWriteExecute only for the CoreCLR service because the .NET JIT requires managed executable mappings while all other systemd protections remain enabled.</title>
<updated>2026-08-28T20:38:41+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-08-28T20:38:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=f92a585d831c8c0304073d2c518bc393957d7ac6'/>
<id>f92a585d831c8c0304073d2c518bc393957d7ac6</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Correct the production Next.js service path for pnpm monorepo standalone output: run the generated apps/web/server.js beneath the preserved standalone module root and document the matching static-asset placement.</title>
<updated>2026-08-28T20:21:59+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-08-28T20:21:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=ff60d2a07a245dc7d709b2fba121371a83c8ca99'/>
<id>ff60d2a07a245dc7d709b2fba121371a83c8ca99</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Separate Progmasoft production hosting from Visual X#: enable portable Next.js standalone output, add hardened loopback-only systemd services for the frontend and account API, define dedicated TLS Nginx virtual hosts with canonical www redirection and ACME handling, document immutable release activation and rollback, and remove workstation-specific tool paths from orchestration.</title>
<updated>2026-08-28T20:21:06+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-08-28T20:21:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=c53ae2482c50b2c29abb0d5ebef5343a80b9b705'/>
<id>c53ae2482c50b2c29abb0d5ebef5343a80b9b705</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
</feed>
