<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Documentation on Heimdall API</title><link>https://artur-rios.github.io/heimdall-api/docs/</link><description>Recent content in Documentation on Heimdall API</description><generator>Hugo</generator><language>en</language><atom:link href="https://artur-rios.github.io/heimdall-api/docs/index.xml" rel="self" type="application/rss+xml"/><item><title>Overview</title><link>https://artur-rios.github.io/heimdall-api/docs/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/overview/</guid><description>&lt;p&gt;Heimdall is a &lt;strong&gt;centralized identity management API&lt;/strong&gt;. Several client systems delegate their&#10;identity concerns to one deployment of it, and each of those systems gets an isolated slice of the&#10;data called a &lt;strong&gt;scope&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;p&gt;Everything else in the model is defined relative to that boundary.&lt;/p&gt;&#10;&lt;h2 id="the-vocabulary"&gt;The vocabulary&lt;a class="td-heading-self-link" href="#the-vocabulary" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Term&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;What it is&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A logical tenant boundary that groups the owners, users, applications, Google Users, and permissions belonging to one client system.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Person&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A registered human identity — name, email, password hash, role, deletion status. A person has &lt;strong&gt;no scope column&lt;/strong&gt;; its relationship to scopes is derived from its role.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;One of &lt;code&gt;SystemAdmin&lt;/code&gt;, &lt;code&gt;ScopeAdmin&lt;/code&gt;, &lt;code&gt;User&lt;/code&gt;. A closed set of three rows.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Scope Owner&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A person with the &lt;code&gt;ScopeAdmin&lt;/code&gt; role — or a &lt;code&gt;SystemAdmin&lt;/code&gt; who chose to — who owns a scope. Many-to-many: a scope has one or more owners, an admin may own several scopes.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Scope User&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A person with the &lt;code&gt;User&lt;/code&gt; role who belongs to &lt;strong&gt;exactly one&lt;/strong&gt; scope.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Application&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A registered non-human identity (another system), in exactly one scope, owned by exactly one Scope Admin who owns that scope.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Google User&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;An identity authenticated by Google rather than a password. Always &lt;code&gt;User&lt;/code&gt;-equivalent, always in exactly one scope, stored in its own table with a direct &lt;code&gt;ScopeId&lt;/code&gt;.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Scope Permission&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A permission defined inside a scope. When &lt;code&gt;IncludeAsJwtClaim&lt;/code&gt; is set, its name is added as a claim on JWTs issued for that scope.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Two-Factor Auth&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;An opt-in second step for a person — authenticator app, email, or both — backed by ten single-use recovery codes. Not available to Google Users.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Logical deletion&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Setting &lt;code&gt;IsDeleted = true&lt;/code&gt;; the row stays.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;Hard deletion&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Removing the row, with defined cascades.&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="how-a-person-relates-to-a-scope"&gt;How a person relates to a scope&lt;a class="td-heading-self-link" href="#how-a-person-relates-to-a-scope" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;This is the single rule that most often surprises newcomers: &lt;code&gt;Person&lt;/code&gt; has no &lt;code&gt;ScopeId&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Getting started</title><link>https://artur-rios.github.io/heimdall-api/docs/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/getting-started/</guid><description>&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="td-heading-self-link" href="#prerequisites" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;.NET 10 SDK&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt; — the API&amp;rsquo;s relational database.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Python 3&lt;/strong&gt; — only to run the migration menu script.&lt;/li&gt;&#10;&lt;li&gt;The pinned EF Core CLI tool. Restore it once after cloning:&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet tool restore&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Building from source, running the test suites and the other contributor tooling are covered in&#10;&lt;a href="../contributing/"&gt;Contributing&lt;/a&gt;&#10;.&lt;/p&gt;&#10;&lt;h2 id="clone"&gt;Clone&lt;a class="td-heading-self-link" href="#clone" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;The documentation site&amp;rsquo;s theme is a git submodule, so clone with submodules (or initialise them&#10;afterwards):&lt;/p&gt;</description></item><item><title>Architecture</title><link>https://artur-rios.github.io/heimdall-api/docs/architecture/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/architecture/</guid><description>&lt;p&gt;Heimdall is a layered (DDD-flavoured) solution with &lt;strong&gt;CQRS&lt;/strong&gt; in the application layer. Reads and&#10;writes travel different objects, through different mediators, into different handlers.&lt;/p&gt;&#10;&lt;h2 id="the-layers"&gt;The layers&lt;a class="td-heading-self-link" href="#the-layers" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;pre class="mermaid"&gt;graph TB&#10; subgraph P[&amp;#34;Presentation — ArturRios.Heimdall.WebApi&amp;#34;]&#10; CTRL[Controllers]&#10; SEC[Security: token issuers, mappers, filters]&#10; MAIL[Email senders]&#10; end&#10; subgraph A[&amp;#34;Application&amp;#34;]&#10; subgraph CMD[&amp;#34;ArturRios.Heimdall.Command&amp;#34;]&#10; CH[Command handlers]&#10; CV[FluentValidation validators]&#10; CS[Services: token, 2FA, email verification]&#10; AUD[Auditing decorator]&#10; end&#10; subgraph QRY[&amp;#34;ArturRios.Heimdall.Query&amp;#34;]&#10; QH[Query handlers]&#10; HC[Health checks]&#10; end&#10; subgraph SHR[&amp;#34;ArturRios.Heimdall.Shared&amp;#34;]&#10; MSG[Messages &amp;#43; status-code maps]&#10; OWN[IScopeOwnershipChecker, IActorAccessor]&#10; end&#10; end&#10; subgraph D[&amp;#34;Domain — ArturRios.Heimdall.Domain&amp;#34;]&#10; ENT[Entities &amp;#43; Roles enum]&#10; end&#10; subgraph I[&amp;#34;Infrastructure — ArturRios.Heimdall.Data&amp;#34;]&#10; CTX[AppDbContext]&#10; MAPS[Entity maps]&#10; MIG[Migrations]&#10; SEED[DatabaseSeeder]&#10; end&#10;&#10; CTRL --&amp;gt; CMD &amp;amp; QRY&#10; CTRL --&amp;gt; MSG&#10; CMD &amp;amp; QRY --&amp;gt; SHR&#10; CMD &amp;amp; QRY --&amp;gt; ENT&#10; CMD &amp;amp; QRY --&amp;gt;|repository interfaces| I&#10; I --&amp;gt; ENT&#10; SEC -.implements.-&amp;gt; CS&#10; MAIL -.implements.-&amp;gt; CS&lt;/pre&gt;&#10;&lt;p&gt;The arrows only ever point inwards or sideways: &lt;strong&gt;Domain depends on nothing&lt;/strong&gt;, and Application&#10;depends on Infrastructure only through repository &lt;em&gt;interfaces&lt;/em&gt; (&lt;code&gt;IAsyncRepository&amp;lt;T, long&amp;gt;&lt;/code&gt;,&#10;&lt;code&gt;IAsyncReadOnlyRepository&amp;lt;T, long&amp;gt;&lt;/code&gt;) that Infrastructure implements. Presentation supplies the concrete&#10;adapters for the abstractions the Application layer declares — &lt;code&gt;IAuthTokenIssuer&lt;/code&gt;,&#10;&lt;code&gt;IEmailVerificationSender&lt;/code&gt;, &lt;code&gt;IGoogleIdTokenVerifier&lt;/code&gt; — which is why swapping Mailgun for logging, or&#10;the real Google verifier for the refusing one, is a start-up registration decision and nothing else.&lt;/p&gt;</description></item><item><title>Domain model</title><link>https://artur-rios.github.io/heimdall-api/docs/domain-model/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/domain-model/</guid><description>&lt;p&gt;The domain layer is intentionally anemic: entities are data holders with properties and navigation&#10;collections. Decisions live in the command and query handlers, so the diagrams below are a map of&#10;&lt;em&gt;shape and relationship&lt;/em&gt;, not of behaviour.&lt;/p&gt;&#10;&lt;h2 id="the-core-model"&gt;The core model&lt;a class="td-heading-self-link" href="#the-core-model" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;The entities every request touches: a scope, the persons related to it through the two join tables,&#10;the applications and Google Users it contains, and the permissions it defines.&lt;/p&gt;</description></item><item><title>API reference</title><link>https://artur-rios.github.io/heimdall-api/docs/api-reference/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/api-reference/</guid><description>&lt;p&gt;Schema-level documentation — every parameter, every request and response body — is published as an&#10;OpenAPI document and rendered by the &lt;a href="../api-explorer/"&gt;API explorer&lt;/a&gt;&#10;, which is the same document a&#10;running instance serves through its own Swagger UI at &lt;code&gt;/swagger&lt;/code&gt;. To &lt;em&gt;call&lt;/em&gt; the endpoints, use that,&#10;or the &lt;a href="../getting-started/#calling-the-api"&gt;&lt;code&gt;.http&lt;/code&gt; files or Bruno collection&lt;/a&gt;&#10; in &lt;code&gt;api-client/&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;This page is the map — what exists, who may call it, and which use case it implements.&lt;/p&gt;</description></item><item><title>API explorer</title><link>https://artur-rios.github.io/heimdall-api/docs/api-explorer/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/api-explorer/</guid><description>&lt;p&gt;Every endpoint the API exposes, rendered from&#10;&lt;a href="https://artur-rios.github.io/heimdall-api/openapi/heimdall.json"&gt;the published OpenAPI document&lt;/a&gt;&#10;. The document is generated from&#10;the controllers themselves — the summaries below are the ones in the source — and regenerated with&#10;&lt;code&gt;python scripts/openapi.py&lt;/code&gt;. It is byte-for-byte what a running instance serves at&#10;&lt;code&gt;/swagger/v1/swagger.json&lt;/code&gt;: both come from the same &lt;code&gt;SwaggerConfiguration&lt;/code&gt;.&lt;/p&gt;&#10;&lt;div class="alert alert-info" role="alert"&gt;&lt;div class="h4 alert-heading" role="heading"&gt;This explorer does not call anything&lt;/div&gt;&#10;&lt;p&gt;The page has no server behind it, so &lt;strong&gt;Try it out&lt;/strong&gt; and &lt;strong&gt;Authorize&lt;/strong&gt; are both gone — a token entered&#10;here could only be attached to a request, and no request can be sent. The padlocks stay, because&#10;which endpoints need one is worth knowing. To call the endpoints, run the API and use its own Swagger&#10;UI at &lt;code&gt;/swagger&lt;/code&gt; — same document, with &lt;strong&gt;Authorize&lt;/strong&gt; and &lt;strong&gt;Try it out&lt;/strong&gt; working — or the&#10;&lt;a href="https://artur-rios.github.io/heimdall-api/docs/getting-started/#calling-the-api"&gt;&lt;code&gt;.http&lt;/code&gt; files or Bruno collection&lt;/a&gt;&#10; in&#10;&lt;code&gt;api-client/&lt;/code&gt;. For what each endpoint means and who may call it in prose, see the&#10;&lt;a href="../api-reference/"&gt;API reference&lt;/a&gt;&#10;.&lt;/p&gt;&#10;&lt;/div&gt;</description></item><item><title>Environments and deployment</title><link>https://artur-rios.github.io/heimdall-api/docs/environments-and-deployment/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/environments-and-deployment/</guid><description>&lt;p&gt;The API runs in four environments. All of them use the same image and the same&#10;&lt;a href="https://github.com/artur-rios/heimdall-api/blob/main/docker-compose.yml" target="_blank" rel="noopener"&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/a&gt;&#10;;&#10;what differs between them is the env file, who deploys it and, mostly, what the host&amp;rsquo;s PostgreSQL&#10;has to be told to allow.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Environment&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Where&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Deployed by&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;code&gt;ASPNETCORE_ENVIRONMENT&lt;/code&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;API at&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Database&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The developer&amp;rsquo;s Windows machine, Docker Desktop&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;By hand, &lt;code&gt;docker compose --env-file docker/local.env up -d --build&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Development&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;heimdall_local&lt;/code&gt;, PostgreSQL on Windows&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;development&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The VPS, &lt;strong&gt;on demand&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Jenkins, on every push to &lt;code&gt;develop&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Development&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;https://heimdall-api-dev.example.com&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;heimdall_development&lt;/code&gt;, PostgreSQL on the VPS&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;homologation&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The VPS, &lt;strong&gt;on demand&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Jenkins, on every push of a &lt;code&gt;release/x.y.z&lt;/code&gt; branch&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Staging&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;https://heimdall-api-hml.example.com&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;heimdall_homologation&lt;/code&gt;, PostgreSQL on the VPS&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;production&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The VPS, always on&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Jenkins, on a green &lt;code&gt;release/x.y.z → main&lt;/code&gt; pull request — it then merges and tags&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Production&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;https://heimdall-api.example.com&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;heimdall&lt;/code&gt;, PostgreSQL on the VPS&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;code&gt;example.com&lt;/code&gt; stands for the real domain throughout this page. In every VPS environment the API is&#10;also served under the web UI&amp;rsquo;s own host at &lt;code&gt;/api/&lt;/code&gt; (&lt;code&gt;https://heimdall-dev.example.com/api/…&lt;/code&gt;,&#10;&lt;code&gt;-hml&lt;/code&gt;, and &lt;code&gt;https://heimdall.example.com/api/…&lt;/code&gt;), which is how heimdall-ui&amp;rsquo;s browser build calls&#10;it without CORS.&lt;/p&gt;</description></item><item><title>Operations</title><link>https://artur-rios.github.io/heimdall-api/docs/operations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://artur-rios.github.io/heimdall-api/docs/operations/</guid><description>&lt;h2 id="migrations"&gt;Migrations&lt;a class="td-heading-self-link" href="#migrations" aria-label="Heading self-link"&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;The schema is managed with &lt;strong&gt;EF Core migrations, applied explicitly&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python scripts/migrations.py&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The script asks which environment file to load (for the connection string), then offers &lt;strong&gt;list&lt;/strong&gt;,&#10;&lt;strong&gt;create (generate)&lt;/strong&gt;, and &lt;strong&gt;apply&lt;/strong&gt;. It needs &lt;code&gt;dotnet tool restore&lt;/code&gt; to have been run once, for the&#10;pinned EF Core CLI tool. Authoring a migration is covered in &lt;a href="../contributing/#migrations"&gt;Contributing&lt;/a&gt;&#10;.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;The API never migrates on startup, and refuses to start when migrations are pending.&lt;/strong&gt; The seeder&#10;checks &lt;code&gt;GetPendingMigrationsAsync&lt;/code&gt; first and throws with the missing migration names:&lt;/p&gt;</description></item></channel></rss>