<feed xmlns='http://www.w3.org/2005/Atom'>
<title>progmaweb/apps/api, branch feature/plugin-identity-from-command</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>Anchor the identity patterns at the end of the text</title>
<updated>2026-11-05T23:45:48+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-11-05T23:45:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=4dbeff2eea0fd2e0ad4c6da3d41f902dcdcf50ce'/>
<id>4dbeff2eea0fd2e0ad4c6da3d41f902dcdcf50ce</id>
<content type='text'>
In .NET, $ also matches before a final line break, so a name or version
ending in one passed the check and was kept with it, giving a catalog entry
and a JAR path the public route never matches. The patterns end in \z.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
In .NET, $ also matches before a final line break, so a name or version
ending in one passed the check and was kept with it, giving a catalog entry
and a JAR path the public route never matches. The patterns end in \z.
</pre>
</div>
</content>
</entry>
<entry>
<title>Take who a Tazel plugin is from the publish command</title>
<updated>2026-11-05T13:49:57+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-11-05T13:49:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=4a00a68309a3605dafedbc522569fc1b2c8aca1e'/>
<id>4a00a68309a3605dafedbc522569fc1b2c8aca1e</id>
<content type='text'>
Tazel no longer puts a publisher, a name or a version into the
descriptor of a plugin JAR (plugin API 3): who a plugin is comes from
ViGet, which records it when the plugin is pushed, or from the path of
a localPlugin.

- viget publish-tazel-plugin takes &lt;Publisher&gt; &lt;Name&gt; &lt;Version&gt; &lt;jar&gt;.
- TazelPluginDescriptor.TryRead and TazelPluginPublishing.PublishAsync
  take the three from the caller and check them as before: one
  identifier each for publisher and name, an exact version of three to
  five numbers.
- A descriptor that holds publisher, name or version is refused, with
  the keys it names.
- ops/README.md shows the new command line.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Tazel no longer puts a publisher, a name or a version into the
descriptor of a plugin JAR (plugin API 3): who a plugin is comes from
ViGet, which records it when the plugin is pushed, or from the path of
a localPlugin.

- viget publish-tazel-plugin takes &lt;Publisher&gt; &lt;Name&gt; &lt;Version&gt; &lt;jar&gt;.
- TazelPluginDescriptor.TryRead and TazelPluginPublishing.PublishAsync
  take the three from the caller and check them as before: one
  identifier each for publisher and name, an exact version of three to
  five numbers.
- A descriptor that holds publisher, name or version is refused, with
  the keys it names.
- ops/README.md shows the new command line.
</pre>
</div>
</content>
</entry>
<entry>
<title>Accept plugin versions of up to five numbers</title>
<updated>2026-11-01T17:16:35+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-11-01T17:16:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=e85e2b2359518f83354c09f9f4a101b7779e6e9c'/>
<id>e85e2b2359518f83354c09f9f4a101b7779e6e9c</id>
<content type='text'>
Tazel takes A.B.C, then .D, .E and -METADATA if needed, so the descriptor
check follows it.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Tazel takes A.B.C, then .D, .E and -METADATA if needed, so the descriptor
check follows it.
</pre>
</div>
</content>
</entry>
<entry>
<title>Accept plugin versions of four numbers</title>
<updated>2026-11-01T13:09:24+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-11-01T13:09:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=02662c79c1f2bcda974da9e1b0ed8c5ac92ee127'/>
<id>02662c79c1f2bcda974da9e1b0ed8c5ac92ee127</id>
<content type='text'>
Progmasoft's releases move to four numbers, and the plugins publish here.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Progmasoft's releases move to four numbers, and the plugins publish here.
</pre>
</div>
</content>
</entry>
<entry>
<title>Provision the reserved accounts on the server</title>
<updated>2026-10-29T12:35:06+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-29T12:35:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=21b515565b73f86477afe436bb3588f3e5929443'/>
<id>21b515565b73f86477afe436bb3588f3e5929443</id>
<content type='text'>
Registration refuses the reserved names Progmasoft and Leitwolf, which are
meant to be created by trusted operations tooling only, and that tooling did
not exist: the Progmasoft account, the publisher of Progmasoft's Tazel
plugins on ViGet, could not be created at all.

- 'accounts provision-reserved &lt;Name&gt; &lt;email&gt;' runs with the configuration
  of the service, like the viget commands. It accepts only the reserved
  names, applies the rules of registration to the email address and the
  password, never replaces an existing account, and brings the account
  tables to the current schema first.
- The password is read from the terminal without echo, twice, or from one
  line of standard input when it is not a terminal; it never appears on a
  command line or in the output.
- AccountNamePolicy.TryNormalizeReserved checks a reserved name;
  TryNormalize, which registration uses, still refuses it.

Tests cover a reserved account created and signing in with its password,
a name that is not reserved, two different passwords, a weak password, an
existing account and the usage line. ops/README.md shows the command with
systemd-run --pty; docs/ACCOUNT-API.md points to it.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Registration refuses the reserved names Progmasoft and Leitwolf, which are
meant to be created by trusted operations tooling only, and that tooling did
not exist: the Progmasoft account, the publisher of Progmasoft's Tazel
plugins on ViGet, could not be created at all.

- 'accounts provision-reserved &lt;Name&gt; &lt;email&gt;' runs with the configuration
  of the service, like the viget commands. It accepts only the reserved
  names, applies the rules of registration to the email address and the
  password, never replaces an existing account, and brings the account
  tables to the current schema first.
- The password is read from the terminal without echo, twice, or from one
  line of standard input when it is not a terminal; it never appears on a
  command line or in the output.
- AccountNamePolicy.TryNormalizeReserved checks a reserved name;
  TryNormalize, which registration uses, still refuses it.

Tests cover a reserved account created and signing in with its password,
a name that is not reserved, two different passwords, a weak password, an
existing account and the usage line. ops/README.md shows the command with
systemd-run --pty; docs/ACCOUNT-API.md points to it.
</pre>
</div>
</content>
</entry>
<entry>
<title>Let the API answer for viget.progmasoft.com</title>
<updated>2026-10-29T10:20:18+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-29T10:20:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=06b384e0b46aa74c36e48dbe86e0e453d975d012'/>
<id>06b384e0b46aa74c36e48dbe86e0e453d975d012</id>
<content type='text'>
Nginx passes https://viget.progmasoft.com/dslplugins/tazel/&lt;Publisher&gt;/&lt;Name&gt;/index.json
to the API with the host of the request, and host filtering refused it with
400: viget.progmasoft.com was missing from AllowedHosts. The index is what
Tazel reads to download a plugin.

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>
Nginx passes https://viget.progmasoft.com/dslplugins/tazel/&lt;Publisher&gt;/&lt;Name&gt;/index.json
to the API with the host of the request, and host filtering refused it with
400: viget.progmasoft.com was missing from AllowedHosts. The index is what
Tazel reads to download a plugin.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Document the ViGet API in full and take zod 4.6.7 for eslint</title>
<updated>2026-10-29T09:42:13+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-29T09:42:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=285e1024da64234b30b0fa425ffd6bf348119db4'/>
<id>285e1024da64234b30b0fa425ffd6bf348119db4</id>
<content type='text'>
The documentation check of the backend found the private members of
ViGet/ without a summary and no page for its namespace:
- Summaries for every field and helper method of the catalog, the
  descriptor, the properties reader, publishing and the endpoints.
- docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md describes the
  namespace.
- The two regular expressions of TazelPluginDescriptor are excluded from the
  reference like AccountNamePolicy.CanonicalPattern: the generator replaces
  their comment with its own.

The dependency audit found GHSA for zod &lt; 4.6.6, which can exhaust memory
while validating oversized constrained collections. Only
eslint-plugin-react-hooks reaches it, and it accepts any zod 4, so an
override takes 4.6.7 until the plugin resolves a fixed release itself.
pnpm audit --audit-level high finds nothing.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The documentation check of the backend found the private members of
ViGet/ without a summary and no page for its namespace:
- Summaries for every field and helper method of the catalog, the
  descriptor, the properties reader, publishing and the endpoints.
- docs/api/namespaces/Progmasoft.Progmaweb.Api.ViGet.md describes the
  namespace.
- The two regular expressions of TazelPluginDescriptor are excluded from the
  reference like AccountNamePolicy.CanonicalPattern: the generator replaces
  their comment with its own.

The dependency audit found GHSA for zod &lt; 4.6.6, which can exhaust memory
while validating oversized constrained collections. Only
eslint-plugin-react-hooks reaches it, and it accepts any zod 4, so an
override takes 4.6.7 until the plugin resolves a fixed release itself.
pnpm audit --audit-level high finds nothing.
</pre>
</div>
</content>
</entry>
<entry>
<title>Add the Tazel plugin catalog of ViGet to the API</title>
<updated>2026-10-29T06:53:57+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-29T06:53:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=58fa0d19d223aaebf9973ebd446c4bb855351ef9'/>
<id>58fa0d19d223aaebf9973ebd446c4bb855351ef9</id>
<content type='text'>
ViGet opens in stages: first only Progmasoft publishes Tazel plugins, later
every account. This adds the catalog and the server-side publishing for the
first stage; nothing publishes through the API yet.

- ViGet/: the catalog of Tazel plugins (in memory, or in PostgreSQL beside
  the accounts with its own numbered schema steps), the descriptor read from
  the JAR (META-INF/tazel-plugin.properties, with a reader of the Java
  properties format, and the ProjectPlugin service entry), and publishing:
  the publisher must be in ViGet:TazelPublishers (Progmasoft for now) and an
  existing account of that exact name, a version is never replaced, names
  that differ only in letter case are refused, and ViGet needs a
  description, an author and a license. The JAR is kept at
  dslplugins/tazel/&lt;Publisher&gt;/&lt;Name&gt;/&lt;Version&gt;/&lt;Name&gt;-&lt;Version&gt;.jar below
  ViGet:ArtifactRoot.
- Read endpoints under /api/v1/viget/dslplugins/tazel: the list, a plugin,
  and index.json, the index Tazel reads.
- 'viget publish-tazel-plugin &lt;jar&gt;' and 'viget yank-tazel-plugin' run with
  the configuration of the service instead of the service.
- The account store can find an account by name.

The pages, the Nginx routes and the deployment follow.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
ViGet opens in stages: first only Progmasoft publishes Tazel plugins, later
every account. This adds the catalog and the server-side publishing for the
first stage; nothing publishes through the API yet.

- ViGet/: the catalog of Tazel plugins (in memory, or in PostgreSQL beside
  the accounts with its own numbered schema steps), the descriptor read from
  the JAR (META-INF/tazel-plugin.properties, with a reader of the Java
  properties format, and the ProjectPlugin service entry), and publishing:
  the publisher must be in ViGet:TazelPublishers (Progmasoft for now) and an
  existing account of that exact name, a version is never replaced, names
  that differ only in letter case are refused, and ViGet needs a
  description, an author and a license. The JAR is kept at
  dslplugins/tazel/&lt;Publisher&gt;/&lt;Name&gt;/&lt;Version&gt;/&lt;Name&gt;-&lt;Version&gt;.jar below
  ViGet:ArtifactRoot.
- Read endpoints under /api/v1/viget/dslplugins/tazel: the list, a plugin,
  and index.json, the index Tazel reads.
- 'viget publish-tazel-plugin &lt;jar&gt;' and 'viget yank-tazel-plugin' run with
  the configuration of the service instead of the service.
- The account store can find an account by name.

The pages, the Nginx routes and the deployment follow.
</pre>
</div>
</content>
</entry>
<entry>
<title>Withdraw an allowance only from the account's own authorizations</title>
<updated>2026-10-23T08:13:27+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-23T08:13:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=06dfa204e4a65c535eb9b3d775a77b5d14522060'/>
<id>06dfa204e4a65c535eb9b3d775a77b5d14522060</id>
<content type='text'>
CodeQL flagged the withdraw endpoint: the authorization was looked up by
the identifier in the address and then checked against the account. The
endpoint now searches only the signed-in account's authorizations for that
identifier and revokes the tokens by the identifier it found, so another
account's allowance is never loaded. A test withdraws as another account
and gets 404.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
CodeQL flagged the withdraw endpoint: the authorization was looked up by
the identifier in the address and then checked against the account. The
endpoint now searches only the signed-in account's authorizations for that
identifier and revokes the tokens by the identifier it found, so another
account's allowance is never loaded. A test withdraws as another account
and gets 404.
</pre>
</div>
</content>
</entry>
<entry>
<title>Move the OAuth pages to Next.js and split the authorize host and the console</title>
<updated>2026-10-23T08:02:39+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-23T08:02:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/progmaweb/commit/?id=9099e9fbb50d27b622362b0c2f0dda5085d1fa03'/>
<id>9099e9fbb50d27b622362b0c2f0dda5085d1fa03</id>
<content type='text'>
Progmasoft OAuth now runs on three hosts:

- credentials.progmasoft.com stays the issuer: discovery documents, key
  set, token, user information, revocation and introspection.
- authorize.progmasoft.com serves the pages people see: the
  authorization and end session endpoints (registered with OpenIddict as
  absolute URIs on that host), sign-in, the consent screen and the
  applications an account has allowed.
- console.progmasoft.com is the Progmasoft Console. Its overview links
  the areas; OAuth 2.1 tokens live under /credentials, with New token,
  then the application type, then its form.

The console has no sign-in of its own and never redirects. It uses the
Progmasoft account session, so production now shares that cookie with
progmasoft.com (__Secure-ProgmasoftSession, Domain=progmasoft.com,
HttpOnly, Secure, SameSite=Lax). Without AccountSessions:CookieDomain the
cookie stays host-only with the __Host- name, as in development and tests.

Application types replace the confidential/public switch: web,
single-page, desktop, mobile and service. Each type has its redirection
rules (RFC 8252), its secret or none, and its grants; only a service
uses client credentials and it registers no address or scope. The type is
kept as an application property and cannot change.

The HTML pages of the API are gone. The pages are Next.js pages in four
languages (en-US, de-DE, ru-RU, he-IL) that call JSON endpoints under
/api/v1/oauth. Requests that change state must come from the page's own
origin; antiforgery forms are no longer needed. The token, revocation and
user information endpoints answer CORS requests from the origins of
single-page applications, without credentials; the discovery documents
and the key set answer every origin.

The host filter keeps every path on its host. Nginx gains the authorize
and console virtual hosts, and the docs describe the hosts, the types and
the shared session.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Progmasoft OAuth now runs on three hosts:

- credentials.progmasoft.com stays the issuer: discovery documents, key
  set, token, user information, revocation and introspection.
- authorize.progmasoft.com serves the pages people see: the
  authorization and end session endpoints (registered with OpenIddict as
  absolute URIs on that host), sign-in, the consent screen and the
  applications an account has allowed.
- console.progmasoft.com is the Progmasoft Console. Its overview links
  the areas; OAuth 2.1 tokens live under /credentials, with New token,
  then the application type, then its form.

The console has no sign-in of its own and never redirects. It uses the
Progmasoft account session, so production now shares that cookie with
progmasoft.com (__Secure-ProgmasoftSession, Domain=progmasoft.com,
HttpOnly, Secure, SameSite=Lax). Without AccountSessions:CookieDomain the
cookie stays host-only with the __Host- name, as in development and tests.

Application types replace the confidential/public switch: web,
single-page, desktop, mobile and service. Each type has its redirection
rules (RFC 8252), its secret or none, and its grants; only a service
uses client credentials and it registers no address or scope. The type is
kept as an application property and cannot change.

The HTML pages of the API are gone. The pages are Next.js pages in four
languages (en-US, de-DE, ru-RU, he-IL) that call JSON endpoints under
/api/v1/oauth. Requests that change state must come from the page's own
origin; antiforgery forms are no longer needed. The token, revocation and
user information endpoints answer CORS requests from the origins of
single-page applications, without credentials; the discovery documents
and the key set answer every origin.

The host filter keeps every path on its host. Nginx gains the authorize
and console virtual hosts, and the docs describe the hosts, the types and
the shared session.
</pre>
</div>
</content>
</entry>
</feed>
