<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://agora.nasqueron.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dereckson</id>
	<title>Nasqueron Agora - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://agora.nasqueron.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dereckson"/>
	<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/Special:Contributions/Dereckson"/>
	<updated>2026-09-21T19:17:38Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.47.0-alpha</generator>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2611</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2611"/>
		<updated>2026-09-20T20:41:01Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Standard operations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
In addition to Salt, Nasqueron manages Infrastructure as Code using Terraform and OpenTofu. We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform.&lt;br /&gt;
&lt;br /&gt;
We provide a &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; wrapper that automatically runs OpenTofu or Terraform based on the directory and mounts the encrypted state directory on the fly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;It&#039;s important to work on Complector, so that all deployments use the shared Terraform state.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Paths ===&lt;br /&gt;
&lt;br /&gt;
Terraform states are stored on a local backend. To protect sensitive infrastructure state (which often contains plaintext secrets and topology data) at rest, we use a split-architecture approach combining [[Operations grimoire/ZFS|ZFS]] and [[Operations grimoire/PEFS|PEFS]] (Private Encrypted File System).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Paths for Terraform / OpenTofu deployment&lt;br /&gt;
|-&lt;br /&gt;
! Path&lt;br /&gt;
! Type&lt;br /&gt;
! Purpose&lt;br /&gt;
! Filesystem Properties&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations/terraform/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Standard Dir&lt;br /&gt;
| Terraform/OpenTofu declarative code&lt;br /&gt;
| Standard ZFS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform.enc&amp;lt;/code&amp;gt;&lt;br /&gt;
| ZFS Dataset&lt;br /&gt;
| Underlying encrypted block storage for PEFS&lt;br /&gt;
| &amp;lt;code&amp;gt;compression=zstd&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform/encrypted&amp;lt;/code&amp;gt;&lt;br /&gt;
| PEFS Mount&lt;br /&gt;
| &#039;&#039;&#039;Encrypted state files.&#039;&#039;&#039; Mounted only during TF operations.&lt;br /&gt;
| AES-256-XTS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform/tf-data&amp;lt;/code&amp;gt;&lt;br /&gt;
| ZFS Dataset&lt;br /&gt;
| &#039;&#039;&#039;Provider cache and modules files.&#039;&#039;&#039; &amp;lt;code&amp;gt;TF_DATA_DIR&amp;lt;/code&amp;gt; target.&lt;br /&gt;
| &amp;lt;code&amp;gt;compression=lz4&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;recordsize=128K&amp;lt;/code&amp;gt; (optimized for Go binaries)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Access control ===&lt;br /&gt;
&lt;br /&gt;
* PEFS operations (mounting, adding keys) are executed as the &amp;lt;code&amp;gt;deploy&amp;lt;/code&amp;gt; user.&lt;br /&gt;
* The wrapper verifies that the invoking user belongs to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;deploy&amp;lt;/code&amp;gt; group before proceeding.&lt;br /&gt;
* The PEFS passphrase is stored in HashiCorp Vault at &amp;lt;code&amp;gt;ops/infra/complector/terraform/pefs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Standard operations ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; wrapper automatically mounts PEFS, fetches the decryption key from Vault, executes the command, and unmounts PEFS upon exit, even on failure or &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can use it exactly like you would use terraform or tofu commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/ovh-ops-backups&lt;br /&gt;
$ tf init&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Collapse card&lt;br /&gt;
|title=Terraform and OpenTofu tf wrapper :: how it works?&lt;br /&gt;
|content=&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
┌─────────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
│                      DEVELOPER TERMINAL ON COMPLECTOR                       │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  $ cd /opt/salt/nasqueron-operations/terraform/&amp;lt;project&amp;gt;                    │&lt;br /&gt;
│  $ tf plan                                                                  │&lt;br /&gt;
│  $ tf apply                                                                 │&lt;br /&gt;
└──────────────────────────────────┬──────────────────────────────────────────┘&lt;br /&gt;
                                   │&lt;br /&gt;
                                  ▼&lt;br /&gt;
┌─────────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
│                        WRAPPER /usr/local/bin/tf                            │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  1. Validate current directory is a top-level project under                 │&lt;br /&gt;
│     /opt/salt/nasqueron-operations/terraform/                               │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  2. Determine runtime:                                                      │&lt;br /&gt;
│     • openbao, ovh-ops-backups  →  terraform                                │&lt;br /&gt;
│     • everything else           →  tofu                                     │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  3. Mount PEFS if not already mounted:                                      │&lt;br /&gt;
│     pefs mount /opt/terraform.enc /opt/terraform/encrypted                  │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  4. Load encryption key if not already in kernel:                           │&lt;br /&gt;
│     vault kv get -field=password ops/infra/complector/terraform/pefs        │&lt;br /&gt;
│	|                                                                     │&lt;br /&gt;
│	└──►  pefs addkey -c /opt/terraform -j -                              │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  5. Prepare directories and environment:                                    │&lt;br /&gt;
│     mkdir -p /opt/terraform/encrypted/tf-state/&amp;lt;project&amp;gt;                    │&lt;br /&gt;
│     export TF_DATA_DIR=/opt/terraform/tf-data/&amp;lt;project&amp;gt;                     │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  6. Execute Terraform/OpenTofu:                                             │&lt;br /&gt;
│     terraform &amp;lt;args&amp;gt;  -or-  tofu &amp;lt;args&amp;gt;                                     │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  7. On exit (trap EXIT):                                                    │&lt;br /&gt;
│     pefs umount /opt/terraform                                              │&lt;br /&gt;
└──────────────────────────────────┬──────────────────────────────────────────┘&lt;br /&gt;
                                   │&lt;br /&gt;
                    ┌──────────────┴──────────────────┐&lt;br /&gt;
                    │                                 │&lt;br /&gt;
                   ▼                                  ▼&lt;br /&gt;
┌───────────────────────────────────────┐   ┌─────────────────────────────────┐&lt;br /&gt;
│        ENCRYPTED VOLUME (PEFS)        │   │         PUBLIC REPOSITORY       │&lt;br /&gt;
│        /opt/terraform.enc/            │   │ /opt/salt/nasqueron-operations/ │&lt;br /&gt;
│                                       │   │                                 │&lt;br /&gt;
│  Mounted at /opt/terraform/encrypted  │   │  terraform/                     │&lt;br /&gt;
│  while tf is running. Sensitive.	│   │    ├── ovh-ops-backups/         │&lt;br /&gt;
│                                       │   │    │     ├── main.tf            │&lt;br /&gt;
│  /o/t/encrypted/tf-state/&amp;lt;project&amp;gt;/   │   │    │     ├── backend.tf         │&lt;br /&gt;
│    └── terraform.tfstate              │   │    │     └── modules/           │&lt;br /&gt;
│    └── terraform.tfstate.bak          │   │    │                            │&lt;br /&gt;
│───────────────────────────────────────│   │    ├── openbao/                 │&lt;br /&gt;
│        WORKING DIRECTORY              │   │    │     ├── main.tf            │&lt;br /&gt;
│        /opt/terraform/                │   │    │     └── ...                │&lt;br /&gt;
│                                       │   │    │                            │&lt;br /&gt;
│  /o/t/tf-data/&amp;lt;project&amp;gt;/ unencrypted  │   │    └── &amp;lt;other-projects&amp;gt;/        │&lt;br /&gt;
│    └── .teraform/                     │   │                                 │&lt;br /&gt;
│    └── providers/                     │   │  Contains only public code.     │&lt;br /&gt;
│    └── modules/                       │   │                                 │&lt;br /&gt;
│                                       │   │                                 │&lt;br /&gt;
└───────────────────────────────────────┘   └─────────────────────────────────┘&lt;br /&gt;
                    │&lt;br /&gt;
                    │  (encrypted at rest by PEFS AES-256-XTS)&lt;br /&gt;
                    │&lt;br /&gt;
                   ▼&lt;br /&gt;
┌─────────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
│                        BACKUP PIPELINE (Restic)                             │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  Restic /opt/terraform.enc                                                  │&lt;br /&gt;
│	│                                                                     │&lt;br /&gt;
│      ▼                                                                      │&lt;br /&gt;
│  OVH Object Storage (S3-compatible)                                         │&lt;br /&gt;
│  • Bucket: nasqueron-backups-amaris                                         │&lt;br /&gt;
│  • Region: EU-WEST-PAR                                                      │&lt;br /&gt;
│  • Versioning: enabled                                                      │&lt;br /&gt;
│  • Object Lock: governance mode, 90-day retention                           │&lt;br /&gt;
│  • Per-client prefix isolation via IAM policies                             │&lt;br /&gt;
└─────────────────────────────────────────────────────────────────────────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Maintenance of .tfstate files ===&lt;br /&gt;
For manual state surgery or moving files, use the explicit mount commands. These bypass the automatic unmount.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Mount PEFS and load keys for manual maintenance&lt;br /&gt;
tf mount&lt;br /&gt;
&lt;br /&gt;
# Perform manual operations (e.g., moving state files)&lt;br /&gt;
cd /opt/terraform/encrypted/tf-states&lt;br /&gt;
# ...&lt;br /&gt;
&lt;br /&gt;
# Unmount when finished&lt;br /&gt;
tf unmount&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Choose between opentofu or terraform ===&lt;br /&gt;
By default, tofu is used.&lt;br /&gt;
&lt;br /&gt;
The wrapper maintains an allowlist of projects that still require HashiCorp Terraform.&lt;br /&gt;
&lt;br /&gt;
The main blocker is &amp;lt;code&amp;gt;hashicorp/vault&amp;lt;/code&amp;gt;, not compiled for FreeBSD on OpenTofu registry.&lt;br /&gt;
&lt;br /&gt;
To add or remove a project from this list, edit the &amp;lt;code&amp;gt;STILL_REQUIRE_TERRAFORM&amp;lt;/code&amp;gt; variable in {{Ops file|roles/salt-primary/opentofu/files/tf.sh}}.&lt;br /&gt;
&lt;br /&gt;
=== New project configuration ===&lt;br /&gt;
&lt;br /&gt;
Every Terraform project must explicitly configure its local backend to write state into the PEFS-encrypted mount. &lt;br /&gt;
&lt;br /&gt;
Add this to your project&#039;s &amp;lt;code&amp;gt;backend.tf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;providers.tf&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;hcl&amp;quot;&amp;gt;&lt;br /&gt;
terraform {&lt;br /&gt;
  backend &amp;quot;local&amp;quot; {&lt;br /&gt;
    path = &amp;quot;/opt/terraform/encrypted/tf-states/&amp;lt;project-name&amp;gt;/terraform.tfstate&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: The provider cache path does not need to be configured in HCL. The wrapper automatically exports &amp;lt;code&amp;gt;TF_DATA_DIR=/opt/terraform/tf-data/&amp;lt;project-name&amp;gt;&amp;lt;/code&amp;gt;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-09-06, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ tf init # install any newly required providers and modules&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note that the benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole has been created in Vault, its credentials need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ tf init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important: Each time you reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This configuration is handled by Terraform.&lt;br /&gt;
&lt;br /&gt;
It requires full access to the public cloud project for backups, as the project is fully configured by these Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get the application key, application secret, and consumer key and write them in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate the file with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== First Terraform configuration (DRP) ==&lt;br /&gt;
=== Initialize Terraform working directories ===&lt;br /&gt;
Terraform and OpenTofu projects are currently deployed from Complector.&lt;br /&gt;
The states are encrypted, and a wrapper decrypts them for each operation.&lt;br /&gt;
&lt;br /&gt;
You need to restore the contents of /opt/terraform.enc from a backup, or if it is not available, initialize a new encrypted directory.&lt;br /&gt;
Currently, the procedure is specific to FreeBSD, as it requires PEFS to encrypt directories.&lt;br /&gt;
&lt;br /&gt;
# On the salt primary server, deploy the full roles/salt-primary, or at least the roles/salt-primary/opentofu unit&lt;br /&gt;
# Ensure pefs is active with &amp;lt;code&amp;gt;kldstat | grep pefs&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ensure Vault is up, then &amp;lt;code&amp;gt;vault kv put ops/infra/complector/terraform/pefs password=$(openssl rand -hex 48)&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run &amp;lt;code&amp;gt;initialize-pefs ops/infra/complector/terraform/pefs /opt/terraform.enc /opt/terraform/encrypted&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The previous version of the Vault secret, if still available, belongs to the previous directory; keep it if you need to restore that directory. However, you need a new password when creating a fresh encrypted directory.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tf init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tf plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tf init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some providers are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
To allow the wrapper to run &amp;lt;code&amp;gt;terraform&amp;lt;/code&amp;gt; commands, add your project directory to the &amp;lt;code&amp;gt;STILL_REQUIRE_TERRAFORM&amp;lt;/code&amp;gt; variable in {{Ops file|roles/salt-primary/opentofu/files/tf.sh}}.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tf state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Sandbox&amp;diff=2610</id>
		<title>User:Dereckson/Sandbox</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Sandbox&amp;diff=2610"/>
		<updated>2026-09-20T20:38:52Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Terraform Secure State Workflow =&lt;br /&gt;
&lt;br /&gt;
{{Infobox software&lt;br /&gt;
| name       = tf-ops / tf wrapper&lt;br /&gt;
| project    = Nasqueron Operations&lt;br /&gt;
| license    = BSD-2-Clause&lt;br /&gt;
| platform   = FreeBSD&lt;br /&gt;
| depends_on = PEFS, HashiCorp Vault, Terraform, OpenTofu, Restic, OVH Object Storage&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
This workflow provides a secure, auditable method for managing Terraform and OpenTofu state on FreeBSD using local encryption. All sensitive infrastructure state is encrypted at rest using [[PEFS]], secrets are injected from [[Vault]] at runtime, and backups are stored in OVH Object Storage with Object Lock for immutability.&lt;br /&gt;
&lt;br /&gt;
The core principle is: &#039;&#039;&#039;no plaintext state or secrets ever exist outside the PEFS encrypted volume&#039;&#039;&#039;, and the volume is mounted only for the duration of a single Terraform operation.&lt;br /&gt;
&lt;br /&gt;
== Architecture ==&lt;br /&gt;
&lt;br /&gt;
=== Schema ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
┌─────────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
│                      DEVELOPER TERMINAL ON COMPLECTOR                       │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  $ cd /opt/salt/nasqueron-operations/terraform/&amp;lt;project&amp;gt;                    │&lt;br /&gt;
│  $ tf plan                                                                  │&lt;br /&gt;
│  $ tf apply                                                                 │&lt;br /&gt;
└──────────────────────────────────┬──────────────────────────────────────────┘&lt;br /&gt;
                                   │&lt;br /&gt;
                                  ▼&lt;br /&gt;
┌─────────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
│                        WRAPPER /usr/local/bin/tf                            │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  1. Validate current directory is a top-level project under                 │&lt;br /&gt;
│     /opt/salt/nasqueron-operations/terraform/                               │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  2. Determine runtime:                                                      │&lt;br /&gt;
│     • openbao, ovh-ops-backups  →  terraform                                │&lt;br /&gt;
│     • everything else           →  tofu                                     │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  3. Mount PEFS if not already mounted:                                      │&lt;br /&gt;
│     pefs mount /opt/terraform.enc /opt/terraform/encrypted                  │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  4. Load encryption key if not already in kernel:                           │&lt;br /&gt;
│     vault kv get -field=password ops/infra/complector/terraform/pefs        │&lt;br /&gt;
│	|                                                                     │&lt;br /&gt;
│	└──►  pefs addkey -c /opt/terraform -j -                              │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  5. Prepare directories and environment:                                    │&lt;br /&gt;
│     mkdir -p /opt/terraform/encrypted/tf-state/&amp;lt;project&amp;gt;                    │&lt;br /&gt;
│     export TF_DATA_DIR=/opt/terraform/tf-data/&amp;lt;project&amp;gt;                     │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  6. Execute Terraform/OpenTofu:                                             │&lt;br /&gt;
│     terraform &amp;lt;args&amp;gt;  -or-  tofu &amp;lt;args&amp;gt;                                     │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  7. On exit (trap EXIT):                                                    │&lt;br /&gt;
│     pefs umount /opt/terraform                                              │&lt;br /&gt;
└──────────────────────────────────┬──────────────────────────────────────────┘&lt;br /&gt;
                                   │&lt;br /&gt;
                    ┌──────────────┴──────────────────┐&lt;br /&gt;
                    │                                 │&lt;br /&gt;
                   ▼                                  ▼&lt;br /&gt;
┌───────────────────────────────────────┐   ┌─────────────────────────────────┐&lt;br /&gt;
│        ENCRYPTED VOLUME (PEFS)        │   │         PUBLIC REPOSITORY       │&lt;br /&gt;
│        /opt/terraform.enc/            │   │ /opt/salt/nasqueron-operations/ │&lt;br /&gt;
│                                       │   │                                 │&lt;br /&gt;
│  Mounted at /opt/terraform/encrypted  │   │  terraform/                     │&lt;br /&gt;
│  while tf is running. Sensitive.	│   │    ├── ovh-ops-backups/         │&lt;br /&gt;
│                                       │   │    │     ├── main.tf            │&lt;br /&gt;
│  /o/t/encrypted/tf-state/&amp;lt;project&amp;gt;/   │   │    │     ├── backend.tf         │&lt;br /&gt;
│    └── terraform.tfstate              │   │    │     └── modules/           │&lt;br /&gt;
│    └── terraform.tfstate.bak          │   │    │                            │&lt;br /&gt;
│───────────────────────────────────────│   │    ├── openbao/                 │&lt;br /&gt;
│        WORKING DIRECTORY              │   │    │     ├── main.tf            │&lt;br /&gt;
│        /opt/terraform/                │   │    │     └── ...                │&lt;br /&gt;
│                                       │   │    │                            │&lt;br /&gt;
│  /o/t/tf-data/&amp;lt;project&amp;gt;/ unencrypted  │   │    └── &amp;lt;other-projects&amp;gt;/        │&lt;br /&gt;
│    └── .teraform/                     │   │                                 │&lt;br /&gt;
│    └── providers/                     │   │  Contains only public code.     │&lt;br /&gt;
│    └── modules/                       │   │                                 │&lt;br /&gt;
│                                       │   │                                 │&lt;br /&gt;
└───────────────────────────────────────┘   └─────────────────────────────────┘&lt;br /&gt;
                    │&lt;br /&gt;
                    │  (encrypted at rest by PEFS AES-256-XTS)&lt;br /&gt;
                    │&lt;br /&gt;
                   ▼&lt;br /&gt;
┌─────────────────────────────────────────────────────────────────────────────┐&lt;br /&gt;
│                        BACKUP PIPELINE (Restic)                             │&lt;br /&gt;
│                                                                             │&lt;br /&gt;
│  Restic /opt/terraform.enc                                                  │&lt;br /&gt;
│	│                                                                     │&lt;br /&gt;
│      ▼                                                                      │&lt;br /&gt;
│  OVH Object Storage (S3-compatible)                                         │&lt;br /&gt;
│  • Bucket: nasqueron-backups-amaris                                         │&lt;br /&gt;
│  • Region: EU-WEST-PAR                                                      │&lt;br /&gt;
│  • Versioning: enabled                                                      │&lt;br /&gt;
│  • Object Lock: governance mode, 90-day retention                           │&lt;br /&gt;
│  • Per-client prefix isolation via IAM policies                             │&lt;br /&gt;
└─────────────────────────────────────────────────────────────────────────────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Components ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Component&lt;br /&gt;
! Role&lt;br /&gt;
! Location&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/usr/local/bin/tf-ops&amp;lt;/code&amp;gt;&lt;br /&gt;
| Wrapper script that orchestrates the secure workflow&lt;br /&gt;
| &amp;lt;code&amp;gt;/usr/local/bin/tf&amp;lt;/code&amp;gt; (symlink or alias)&lt;br /&gt;
|-&lt;br /&gt;
| [[PEFS]]&lt;br /&gt;
| AES-256-XTS stacked filesystem for encrypting state at rest&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform.enc&amp;lt;/code&amp;gt; → mounted at &amp;lt;code&amp;gt;/opt/terraform&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| [[Vault]]&lt;br /&gt;
| Stores the PEFS passphrase and all infrastructure secrets&lt;br /&gt;
| Path: &amp;lt;code&amp;gt;ops/infra/complector/terraform/pefs&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Terraform / OpenTofu&lt;br /&gt;
| Infrastructure-as-code engine&lt;br /&gt;
| Selected per-project by the wrapper&lt;br /&gt;
|-&lt;br /&gt;
| Restic&lt;br /&gt;
| Incremental, deduplicating backup client&lt;br /&gt;
| Backs up the raw &amp;lt;code&amp;gt;/opt/terraform.enc&amp;lt;/code&amp;gt; directory&lt;br /&gt;
|-&lt;br /&gt;
| OVH Object Storage&lt;br /&gt;
| S3-compatible remote storage with Object Lock&lt;br /&gt;
| Buckets: &amp;lt;code&amp;gt;nasqueron-backups-{amaris,darak,vakor}&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Security Model ==&lt;br /&gt;
&lt;br /&gt;
=== Encryption Layers ===&lt;br /&gt;
&lt;br /&gt;
The workflow applies defense in depth across three layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer&lt;br /&gt;
! Technology&lt;br /&gt;
! Protects Against&lt;br /&gt;
|-&lt;br /&gt;
| At-rest encryption&lt;br /&gt;
| PEFS (AES-256-XTS)&lt;br /&gt;
| Disk theft, unauthorized filesystem access, unencrypted backups&lt;br /&gt;
|-&lt;br /&gt;
| Secret injection&lt;br /&gt;
| HashiCorp Vault (KV v2)&lt;br /&gt;
| Plaintext passwords in scripts, config files, or shell history&lt;br /&gt;
|-&lt;br /&gt;
| Remote backup immutability&lt;br /&gt;
| OVH Object Lock (Governance)&lt;br /&gt;
| Ransomware deletion of backups, accidental pruning&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Secret Flow ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Vault KV v2                    PEFS Kernel                    Terraform&lt;br /&gt;
(ops/infra/complector/    ──►  (decrypts /opt/terraform.enc   ──►  reads/writes&lt;br /&gt;
 terraform/pefs)                in-memory, never touches           encrypted state&lt;br /&gt;
                                disk in plaintext)                 inside PEFS mount&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PEFS passphrase is never written to disk. It flows directly from the Vault API into &amp;lt;code&amp;gt;pefs addkey&amp;lt;/code&amp;gt; via a Unix pipe (&amp;lt;code&amp;gt;-j -&amp;lt;/code&amp;gt; reads from stdin). The plaintext passphrase exists only in process memory for the duration of the &amp;lt;code&amp;gt;addkey&amp;lt;/code&amp;gt; system call.&lt;br /&gt;
&lt;br /&gt;
=== Trust Boundaries ===&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;public repository&#039;&#039;&#039; contains only declarative infrastructure code. It is safe for Salt to read, for CI to lint, and for developers to clone.&lt;br /&gt;
* The &#039;&#039;&#039;PEFS volume&#039;&#039;&#039; contains all sensitive runtime artifacts (state files, provider caches, module downloads). It is encrypted and unmounted by default.&lt;br /&gt;
* The &#039;&#039;&#039;Vault server&#039;&#039;&#039; is the single source of truth for the PEFS passphrase. Access is governed by Vault ACL policies.&lt;br /&gt;
* The &#039;&#039;&#039;OVH backup&#039;&#039;&#039; stores only the encrypted PEFS volume. The backup provider never sees plaintext state or secrets.&lt;br /&gt;
&lt;br /&gt;
== The &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; Wrapper Script ==&lt;br /&gt;
&lt;br /&gt;
=== Usage ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# From any top-level Terraform project directory:&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/ovh-ops-backups&lt;br /&gt;
$ tf init&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply -auto-approve&lt;br /&gt;
$ tf state list&lt;br /&gt;
$ tf output -json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script must be executed from within a &#039;&#039;&#039;top-level&#039;&#039;&#039; subdirectory of &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations/terraform/&amp;lt;/code&amp;gt;. Running it from a nested module directory will result in an error.&lt;br /&gt;
&lt;br /&gt;
=== Behavior ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Step&lt;br /&gt;
! Action&lt;br /&gt;
! Failure Handling&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Validate working directory&lt;br /&gt;
| Exits with error code 1 if not in a valid project directory&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Select Terraform or OpenTofu binary&lt;br /&gt;
| Falls back to &amp;lt;code&amp;gt;tofu&amp;lt;/code&amp;gt; unless the project is in the whitelist&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Mount PEFS if unmounted&lt;br /&gt;
| Exits with error code 2 if &amp;lt;code&amp;gt;pefs mount&amp;lt;/code&amp;gt; fails&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Load key from Vault if not in kernel&lt;br /&gt;
| Exits with error code 1 if Vault or &amp;lt;code&amp;gt;pefs addkey&amp;lt;/code&amp;gt; fails&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Create state and cache directories&lt;br /&gt;
| Uses &amp;lt;code&amp;gt;mkdir -p&amp;lt;/code&amp;gt;; idempotent&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Execute Terraform/OpenTofu&lt;br /&gt;
| Inherits the child process exit code&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Unmount PEFS via &amp;lt;code&amp;gt;trap EXIT&amp;lt;/code&amp;gt;&lt;br /&gt;
| Always runs, even on Ctrl+C or script failure&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Runtime Selection ===&lt;br /&gt;
&lt;br /&gt;
The wrapper maintains a whitelist of projects that still require the HashiCorp &amp;lt;code&amp;gt;terraform&amp;lt;/code&amp;gt; binary. All other projects default to &amp;lt;code&amp;gt;tofu&amp;lt;/code&amp;gt; (OpenTofu).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
STILL_REQUIRE_TERRAFORM=&amp;quot;openbao ovh-ops-backups&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This allows a gradual migration from Terraform to OpenTofu without changing the developer workflow.&lt;br /&gt;
&lt;br /&gt;
== Configuration Reference ==&lt;br /&gt;
&lt;br /&gt;
=== Repository Structure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/opt/salt/nasqueron-operations/&lt;br /&gt;
└── terraform/&lt;br /&gt;
    ├── ovh-ops-backups/          # Uses terraform (whitelisted)&lt;br /&gt;
    │   ├── main.tf&lt;br /&gt;
    │   ├── backend.tf            # Must point to PEFS state path&lt;br /&gt;
    │   └── modules/&lt;br /&gt;
    ├── openbao/                  # Uses terraform (whitelisted)&lt;br /&gt;
    │   └── ...&lt;br /&gt;
    ├── some-other-project/       # Uses tofu (default)&lt;br /&gt;
    │   └── ...&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PEFS Layout ===&lt;br /&gt;
&lt;br /&gt;
When mounted, the PEFS volume exposes the following structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/opt/terraform/&lt;br /&gt;
├── tf-state/&lt;br /&gt;
│   ├── ovh-ops-backups/&lt;br /&gt;
│   │   ├── terraform.tfstate&lt;br /&gt;
│   │   └── terraform.tfstate.backup&lt;br /&gt;
│   ├── openbao/&lt;br /&gt;
│   │   └── terraform.tfstate&lt;br /&gt;
│   └── ...&lt;br /&gt;
└── tf-data/&lt;br /&gt;
    ├── ovh-ops-backups/&lt;br /&gt;
    │   └── .terraform/&lt;br /&gt;
    │       ├── providers/&lt;br /&gt;
    │       └── modules/&lt;br /&gt;
    ├── openbao/&lt;br /&gt;
    │   └── .terraform/&lt;br /&gt;
    └── ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vault Paths ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Path&lt;br /&gt;
! Field&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ops/infra/complector/terraform/pefs&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;&lt;br /&gt;
| PEFS passphrase used to decrypt &amp;lt;code&amp;gt;/opt/terraform.enc&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ops/secrets/backups/ovh/s3/&amp;amp;lt;account&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;access_key&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;secret_key&amp;lt;/code&amp;gt;&lt;br /&gt;
| Per-client S3 credentials for Restic backups&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Terraform Backend Configuration ===&lt;br /&gt;
&lt;br /&gt;
Each project must configure its local backend to write state into the PEFS-mounted directory. Example &amp;lt;code&amp;gt;backend.tf&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;hcl&amp;quot;&amp;gt;&lt;br /&gt;
terraform {&lt;br /&gt;
  backend &amp;quot;local&amp;quot; {&lt;br /&gt;
    path = &amp;quot;/opt/terraform/tf-state/ovh-ops-backups/terraform.tfstate&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Warning|If this path is not configured, Terraform will write &amp;lt;code&amp;gt;terraform.tfstate&amp;lt;/code&amp;gt; in plaintext to the current working directory, defeating the entire security model.}}&lt;br /&gt;
&lt;br /&gt;
== Operations ==&lt;br /&gt;
&lt;br /&gt;
=== Initial Setup ===&lt;br /&gt;
&lt;br /&gt;
# Create the PEFS encrypted volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir -p /opt/terraform.enc&lt;br /&gt;
pefs mount /opt/terraform.enc /opt/terraform&lt;br /&gt;
pefs addkey -c /opt/terraform&lt;br /&gt;
# Store the passphrase in Vault:&lt;br /&gt;
vault kv put ops/infra/complector/terraform/pefs password=&amp;quot;&amp;lt;your-passphrase&amp;gt;&amp;quot;&lt;br /&gt;
pefs umount /opt/terraform&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Install the wrapper script to &amp;lt;code&amp;gt;/usr/local/bin/tf-ops&amp;lt;/code&amp;gt; and create a symlink:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chmod +x /usr/local/bin/tf-ops&lt;br /&gt;
ln -s /usr/local/bin/tf-ops /usr/local/bin/tf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Ensure Vault CLI is authenticated:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vault login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Daily Usage ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ pm go terraform-ovh-ops-backups   # or: cd /opt/.../terraform/ovh-ops-backups&lt;br /&gt;
$ tf init&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The wrapper handles mounting, key loading, and unmounting transparently. No manual PEFS commands are required.&lt;br /&gt;
&lt;br /&gt;
=== Backup and Restore ===&lt;br /&gt;
&lt;br /&gt;
Backups are performed by Restic against the raw encrypted volume:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
restic -r s3:https://s3.eu-west-par.io.cloud.ovh.net/nasqueron-backups-amaris \&lt;br /&gt;
       backup /opt/terraform.enc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To restore on a new machine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
restic -r s3:https://s3.eu-west-par.io.cloud.ovh.net/nasqueron-backups-amaris \&lt;br /&gt;
       restore latest --target /opt/terraform.enc&lt;br /&gt;
&lt;br /&gt;
pefs mount /opt/terraform.enc /opt/terraform&lt;br /&gt;
vault kv get -field=password ops/infra/complector/terraform/pefs | \&lt;br /&gt;
    pefs addkey -c /opt/terraform -j -&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disaster Recovery ===&lt;br /&gt;
&lt;br /&gt;
If the Vault server is unavailable, the PEFS volume cannot be unlocked. Ensure Vault itself is backed up and highly available. The PEFS passphrase should also be stored in a secure offline location (e.g., a hardware security module, a sealed envelope in a safe, or an encrypted password manager) as an emergency recovery path.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
* [[PEFS]] — Private Encrypted File System for FreeBSD&lt;br /&gt;
* [https://pefs.io/ PEFS official documentation]&lt;br /&gt;
* [https://pefs.io/static/pefs-devsummit-2011-oct.pdf PEFS Developer Summit 2011 presentation]&lt;br /&gt;
* [[Vault]] — HashiCorp Vault secrets management&lt;br /&gt;
* [https://opentofu.org/ OpenTofu]&lt;br /&gt;
* [https://restic.net/ Restic backup tool]&lt;br /&gt;
* [https://www.ovhcloud.com/en-ie/object-storage/ OVH Object Storage]&lt;br /&gt;
&lt;br /&gt;
[[Category:Infrastructure]]&lt;br /&gt;
[[Category:Security]]&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=MediaWiki:Common.css&amp;diff=2609</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=MediaWiki:Common.css&amp;diff=2609"/>
		<updated>2026-09-20T20:38:09Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Code in JetBrains Mono&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Source code */&lt;br /&gt;
&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;JetBrains Mono&amp;quot;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
  src: url(&amp;quot;https://assets.nasqueron.org/webfonts/JetBrainsMono/JetBrainsMono-Regular.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &amp;quot;JetBrains Mono&amp;quot;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  font-display: swap;&lt;br /&gt;
  src: url(&amp;quot;https://assets.nasqueron.org/webfonts/JetBrainsMono/JetBrainsMono-Bold.woff2&amp;quot;) format(&amp;quot;woff2&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
code, tt, kbd, pre, samp {&lt;br /&gt;
	font-family: &amp;quot;JetBrains Mono&amp;quot;, ui-monospace, monospace;&lt;br /&gt;
	font-size: 0.9em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Call for actions button  */&lt;br /&gt;
&lt;br /&gt;
.btn {&lt;br /&gt;
	-webkit-border-radius: 6;&lt;br /&gt;
	-moz-border-radius: 6;&lt;br /&gt;
	border-radius: 6px;&lt;br /&gt;
	color: #ffffff;&lt;br /&gt;
	font-size: 1.4em;&lt;br /&gt;
	background: #4d7b94;&lt;br /&gt;
	padding: 10px 20px 10px 20px;&lt;br /&gt;
	text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.btn a, .btn a:hover, .btn a:visited {&lt;br /&gt;
	color: white !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.btn:hover {&lt;br /&gt;
	background: #6caacc;&lt;br /&gt;
	text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Inline code  */&lt;br /&gt;
&lt;br /&gt;
.inline-code, .inline-code div,  .inline-code pre {&lt;br /&gt;
	display: inline-table;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Technical Wiki Alert&lt;br /&gt;
   Aesthetic: industrial / editorial&lt;br /&gt;
   Typography: IBM Plex Sans + IBM Plex Mono&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
    /* Light mode */&lt;br /&gt;
    --alert-danger: #b42318;&lt;br /&gt;
    --alert-danger-dark: #7a1710;&lt;br /&gt;
    --alert-danger-bg: #fff4f1;&lt;br /&gt;
    --alert-danger-ink: #32110d;&lt;br /&gt;
    --alert-danger-line: #d8aaa3;&lt;br /&gt;
    --alert-accent: #f04438;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Base component&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
.alert {&lt;br /&gt;
    --alert-border: var(--alert-danger);&lt;br /&gt;
    --alert-bg: var(--alert-danger-bg);&lt;br /&gt;
    --alert-ink: var(--alert-danger-ink);&lt;br /&gt;
&lt;br /&gt;
    position: relative;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
&lt;br /&gt;
    margin: 1.5rem 0;&lt;br /&gt;
    padding: 1rem 1.25rem 1rem 1.5rem;&lt;br /&gt;
&lt;br /&gt;
    background:&lt;br /&gt;
        linear-gradient(&lt;br /&gt;
            90deg,&lt;br /&gt;
            color-mix(in srgb, var(--alert-border) 8%, transparent) 0,&lt;br /&gt;
            color-mix(in srgb, var(--alert-border) 8%, transparent) 4px,&lt;br /&gt;
            transparent 4px&lt;br /&gt;
        ),&lt;br /&gt;
        var(--alert-bg);&lt;br /&gt;
&lt;br /&gt;
    color: var(--alert-ink);&lt;br /&gt;
&lt;br /&gt;
    border: 1px solid var(--alert-danger-line);&lt;br /&gt;
    border-left: 5px solid var(--alert-border);&lt;br /&gt;
&lt;br /&gt;
    border-radius: 0;&lt;br /&gt;
&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Sans&amp;quot;, sans-serif;&lt;br /&gt;
    font-size: 0.95rem;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    line-height: 1.6;&lt;br /&gt;
&lt;br /&gt;
    box-shadow:&lt;br /&gt;
        4px 4px 0 rgba(50, 17, 13, 0.08);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Classification label */&lt;br /&gt;
&lt;br /&gt;
.alert::before {&lt;br /&gt;
    content: &amp;quot;ALERT&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    display: block;&lt;br /&gt;
    margin-bottom: 0.35rem;&lt;br /&gt;
&lt;br /&gt;
    color: var(--alert-danger);&lt;br /&gt;
&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Mono&amp;quot;, monospace;&lt;br /&gt;
    font-size: 0.68rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    letter-spacing: 0.14em;&lt;br /&gt;
    line-height: 1;&lt;br /&gt;
&lt;br /&gt;
    text-transform: uppercase;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Content */&lt;br /&gt;
&lt;br /&gt;
.alert strong:first-child {&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Links */&lt;br /&gt;
&lt;br /&gt;
.alert a {&lt;br /&gt;
    color: var(--alert-danger-dark);&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    text-decoration-thickness: 2px;&lt;br /&gt;
    text-underline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert a:hover {&lt;br /&gt;
    color: var(--alert-danger);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert a:focus-visible {&lt;br /&gt;
    outline: 2px solid var(--alert-accent);&lt;br /&gt;
    outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Dark mode — explicitly configured&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night {&lt;br /&gt;
    --alert-danger: #e5483e;&lt;br /&gt;
    --alert-danger-dark: #ff6b61;&lt;br /&gt;
    --alert-danger-bg: #211513;&lt;br /&gt;
    --alert-danger-ink: #f6e7e4;&lt;br /&gt;
    --alert-danger-line: #5c2b27;&lt;br /&gt;
    --alert-accent: #ff5a52;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert {&lt;br /&gt;
    box-shadow:&lt;br /&gt;
        4px 4px 0 rgba(0, 0, 0, 0.25);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Dark mode — follows system preference&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
    html.skin-theme-clientpref-os {&lt;br /&gt;
        --alert-danger: #e5483e;&lt;br /&gt;
        --alert-danger-dark: #ff6b61;&lt;br /&gt;
        --alert-danger-bg: #211513;&lt;br /&gt;
        --alert-danger-ink: #f6e7e4;&lt;br /&gt;
        --alert-danger-line: #5c2b27;&lt;br /&gt;
        --alert-accent: #ff5a52;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    html.skin-theme-clientpref-os .alert {&lt;br /&gt;
        box-shadow:&lt;br /&gt;
            4px 4px 0 rgba(0, 0, 0, 0.25);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Reduced motion&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: no-preference) {&lt;br /&gt;
    .alert {&lt;br /&gt;
        animation: alert-in 220ms ease-out both;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@keyframes alert-in {&lt;br /&gt;
    from {&lt;br /&gt;
        opacity: 0;&lt;br /&gt;
        transform: translateX(-8px);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    to {&lt;br /&gt;
        opacity: 1;&lt;br /&gt;
        transform: translateX(0);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Alert variants&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
.alert--warning {&lt;br /&gt;
    --alert-danger: #9a6700;&lt;br /&gt;
    --alert-danger-dark: #704800;&lt;br /&gt;
    --alert-bg: #fff9eb;&lt;br /&gt;
    --alert-ink: #3d2a0a;&lt;br /&gt;
    --alert-danger-line: #dec58e;&lt;br /&gt;
    --alert-accent: #d69e2e;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--info {&lt;br /&gt;
    --alert-danger: #1769aa;&lt;br /&gt;
    --alert-danger-dark: #0f4f82;&lt;br /&gt;
    --alert-bg: #eef7ff;&lt;br /&gt;
    --alert-ink: #102a43;&lt;br /&gt;
    --alert-danger-line: #a9cce8;&lt;br /&gt;
    --alert-accent: #2b8ed6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--success {&lt;br /&gt;
    --alert-danger: #287a4b;&lt;br /&gt;
    --alert-danger-dark: #1b6038;&lt;br /&gt;
    --alert-bg: #eefbf3;&lt;br /&gt;
    --alert-ink: #102a1b;&lt;br /&gt;
    --alert-danger-line: #a8d4b8;&lt;br /&gt;
    --alert-accent: #38a169;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Dark variants */&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert--warning,&lt;br /&gt;
html.skin-theme-clientpref-os .alert--warning {&lt;br /&gt;
    --alert-danger: #d9a441;&lt;br /&gt;
    --alert-danger-dark: #f0c66b;&lt;br /&gt;
    --alert-bg: #211c11;&lt;br /&gt;
    --alert-ink: #f4ead5;&lt;br /&gt;
    --alert-danger-line: #5b4928;&lt;br /&gt;
    --alert-accent: #e5b14d;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert--info,&lt;br /&gt;
html.skin-theme-clientpref-os .alert--info {&lt;br /&gt;
    --alert-danger: #4da3df;&lt;br /&gt;
    --alert-danger-dark: #79c4f2;&lt;br /&gt;
    --alert-bg: #111d27;&lt;br /&gt;
    --alert-ink: #e1edf5;&lt;br /&gt;
    --alert-danger-line: #294b61;&lt;br /&gt;
    --alert-accent: #5db5ed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert--success,&lt;br /&gt;
html.skin-theme-clientpref-os .alert--success {&lt;br /&gt;
    --alert-danger: #55b87a;&lt;br /&gt;
    --alert-danger-dark: #7bd39a;&lt;br /&gt;
    --alert-bg: #112019;&lt;br /&gt;
    --alert-ink: #e1f0e6;&lt;br /&gt;
    --alert-danger-line: #28523a;&lt;br /&gt;
    --alert-accent: #62c889;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Variant labels&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
.alert--warning::before {&lt;br /&gt;
    content: &amp;quot;WARNING&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--info::before {&lt;br /&gt;
    content: &amp;quot;INFO&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--success::before {&lt;br /&gt;
    content: &amp;quot;OK&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Colors from 2017 palette&lt;br /&gt;
    &lt;br /&gt;
    See https://devcentral.nasqueron.org/M11 or [[Design/2017 colors]]&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
.color-magnetic-one {&lt;br /&gt;
	/* Cyberspace gray */&lt;br /&gt;
	color: #44484D;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-two {&lt;br /&gt;
	/* Anchors Aweigh blue */&lt;br /&gt;
	color: #2B3441;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-three {&lt;br /&gt;
	/* Niagara blue — Pantone 17-4123 */&lt;br /&gt;
	color: #5587A2; &lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-four {&lt;br /&gt;
	/* Primrose yellow — Pantone 13-0755 */&lt;br /&gt;
	color: #F6D258;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-five {&lt;br /&gt;
	/* Green from http://www.elledecor.com/design-decorate/color/g3175/color-trends/ */&lt;br /&gt;
	color: #67947D;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Responsive blocks.&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
.block {&lt;br /&gt;
	margin: 0 0;&lt;br /&gt;
	padding: 5vh 5vw;&lt;br /&gt;
	&lt;br /&gt;
	border: #808080 solid 5px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col {&lt;br /&gt;
	position: relative;&lt;br /&gt;
	display:inline;&lt;br /&gt;
	float: left;&lt;br /&gt;
	margin-right: 1em;&lt;br /&gt;
	padding: 1vw;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col-quarter {&lt;br /&gt;
	width: 20%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col-half {&lt;br /&gt;
	width: 40%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block {&lt;br /&gt;
	min-height: 50vh;&lt;br /&gt;
	background-color: #44484D;&lt;br /&gt;
	color: white;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block dt {&lt;br /&gt;
    color: #67947D;&lt;br /&gt;
    font-family: &#039;Segoe UI&#039;, &#039;Segoe UI Emoji&#039;, &#039;Segoe UI Symbol&#039;, &#039;Lato&#039;, &#039;Liberation Sans&#039;, &#039;Noto Sans&#039;, &#039;Helvetica Neue&#039;, &#039;Helvetica&#039;, sans-serif;&lt;br /&gt;
    font-size: 1.375em;&lt;br /&gt;
    margin-bottom: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block a {&lt;br /&gt;
    color: #F6D258 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	Variable Grid System.&lt;br /&gt;
	Learn more ~ http://www.spry-soft.com/grids/&lt;br /&gt;
	Based on 960 Grid System - http://960.gs/&lt;br /&gt;
&lt;br /&gt;
	Licensed under GPL and MIT.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Containers&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
.container_12 {&lt;br /&gt;
	margin-left: auto;&lt;br /&gt;
	margin-right: auto;&lt;br /&gt;
	width: 960px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; Global&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.grid_1,&lt;br /&gt;
.grid_2,&lt;br /&gt;
.grid_3,&lt;br /&gt;
.grid_4,&lt;br /&gt;
.grid_5,&lt;br /&gt;
.grid_6,&lt;br /&gt;
.grid_7,&lt;br /&gt;
.grid_8,&lt;br /&gt;
.grid_9,&lt;br /&gt;
.grid_10,&lt;br /&gt;
.grid_11,&lt;br /&gt;
.grid_12 {&lt;br /&gt;
	display:inline;&lt;br /&gt;
	float: left;&lt;br /&gt;
	position: relative;&lt;br /&gt;
	margin-left: 10px;&lt;br /&gt;
	margin-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; Children (Alpha ~ First, Omega ~ Last)&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.alpha {&lt;br /&gt;
	margin-left: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.omega {&lt;br /&gt;
	margin-right: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_1 {&lt;br /&gt;
	width:60px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_2 {&lt;br /&gt;
	width:140px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_3 {&lt;br /&gt;
	width:220px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_4 {&lt;br /&gt;
	width:300px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_5 {&lt;br /&gt;
	width:380px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_6 {&lt;br /&gt;
	width:460px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_7 {&lt;br /&gt;
	width:540px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_8 {&lt;br /&gt;
	width:620px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_9 {&lt;br /&gt;
	width:700px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_10 {&lt;br /&gt;
	width:780px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_11 {&lt;br /&gt;
	width:860px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_12 {&lt;br /&gt;
	width:940px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Prefix Extra Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_1 {&lt;br /&gt;
	padding-left:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_2 {&lt;br /&gt;
	padding-left:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_3 {&lt;br /&gt;
	padding-left:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_4 {&lt;br /&gt;
	padding-left:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_5 {&lt;br /&gt;
	padding-left:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_6 {&lt;br /&gt;
	padding-left:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_7 {&lt;br /&gt;
	padding-left:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_8 {&lt;br /&gt;
	padding-left:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_9 {&lt;br /&gt;
	padding-left:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_10 {&lt;br /&gt;
	padding-left:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_11 {&lt;br /&gt;
	padding-left:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Suffix Extra Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_1 {&lt;br /&gt;
	padding-right:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_2 {&lt;br /&gt;
	padding-right:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_3 {&lt;br /&gt;
	padding-right:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_4 {&lt;br /&gt;
	padding-right:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_5 {&lt;br /&gt;
	padding-right:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_6 {&lt;br /&gt;
	padding-right:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_7 {&lt;br /&gt;
	padding-right:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_8 {&lt;br /&gt;
	padding-right:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_9 {&lt;br /&gt;
	padding-right:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_10 {&lt;br /&gt;
	padding-right:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_11 {&lt;br /&gt;
	padding-right:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Push Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_1 {&lt;br /&gt;
	left:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_2 {&lt;br /&gt;
	left:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_3 {&lt;br /&gt;
	left:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_4 {&lt;br /&gt;
	left:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_5 {&lt;br /&gt;
	left:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_6 {&lt;br /&gt;
	left:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_7 {&lt;br /&gt;
	left:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_8 {&lt;br /&gt;
	left:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_9 {&lt;br /&gt;
	left:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_10 {&lt;br /&gt;
	left:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_11 {&lt;br /&gt;
	left:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Pull Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_1 {&lt;br /&gt;
	left:-80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_2 {&lt;br /&gt;
	left:-160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_3 {&lt;br /&gt;
	left:-240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_4 {&lt;br /&gt;
	left:-320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_5 {&lt;br /&gt;
	left:-400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_6 {&lt;br /&gt;
	left:-480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_7 {&lt;br /&gt;
	left:-560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_8 {&lt;br /&gt;
	left:-640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_9 {&lt;br /&gt;
	left:-720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_10 {&lt;br /&gt;
	left:-800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_11 {&lt;br /&gt;
	left:-880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Clear Floated Elements&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* http://sonspring.com/journal/clearing-floats */&lt;br /&gt;
&lt;br /&gt;
.clear {&lt;br /&gt;
	clear: both;&lt;br /&gt;
	display: block;&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
	visibility: hidden;&lt;br /&gt;
	width: 0;&lt;br /&gt;
	height: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* http://perishablepress.com/press/2008/02/05/lessons-learned-concerning-the-clearfix-css-hack */&lt;br /&gt;
&lt;br /&gt;
.clearfix:after {&lt;br /&gt;
	clear: both;&lt;br /&gt;
	content: &#039; &#039;;&lt;br /&gt;
	display: block;&lt;br /&gt;
	font-size: 0;&lt;br /&gt;
	line-height: 0;&lt;br /&gt;
	visibility: hidden;&lt;br /&gt;
	width: 0;&lt;br /&gt;
	height: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.clearfix {&lt;br /&gt;
	display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
* html .clearfix {&lt;br /&gt;
	height: 1%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.clearfix {&lt;br /&gt;
	display: block;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2608</id>
		<title>Devserver reference</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2608"/>
		<updated>2026-09-13T11:40:04Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Modern Neovim support */ Comment in T295 quotes basic settings&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nasqueron Operations SIG maintains &#039;&#039;&#039;devservers&#039;&#039;&#039;. They offer a remote development environment for Nasqueron and open source projects.&lt;br /&gt;
&lt;br /&gt;
== General information ==&lt;br /&gt;
=== What server to use? ===&lt;br /&gt;
&lt;br /&gt;
* WindRiver, for any general-purpose task&lt;br /&gt;
* Ysul, for webserver-legacy sites&lt;br /&gt;
* Dwellers, for Docker development&lt;br /&gt;
* Eglide, if you need an IRC session&lt;br /&gt;
&lt;br /&gt;
=== How to get access? ===&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/3/&lt;br /&gt;
  | text = Request account&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
You can ask it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
==== Do it yourself ====&lt;br /&gt;
&lt;br /&gt;
You&#039;re encouraged to submit a change with the configuration for your own access. That helps to speed up the process and ensure the right information is there:&lt;br /&gt;
&lt;br /&gt;
* Familiar with Git and our contributions workflow? follow NOG instructions at [[Operations grimoire/Create and revoke user accounts on Salt servers]] and send a commit to DevCentral against our operations repository.&lt;br /&gt;
* Newcomer ? We also have a full guide in French explaining step by step how to create that change on a Windows system and send it to review: [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Groups to use&lt;br /&gt;
|-&lt;br /&gt;
! Server !! Group name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Ysul / WindRiver || nasquenautes || for general purpose development&lt;br /&gt;
|-&lt;br /&gt;
| Dwellers || nasqueron-dev-docker || for Docker development&lt;br /&gt;
|-&lt;br /&gt;
| Eglide || shell || for IRC purpose&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Userland ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Normally, devservers run under FreeBSD. That means your userland is a BSD one, and some commands arguments can be different if you&#039;re used to Linux. GNU coretools utilities are installed and prefixed by the letter g, e.g. gmake or gsed.&lt;br /&gt;
&lt;br /&gt;
The most useful GNU commands are:&lt;br /&gt;
* gmake when the Makefile has been written for GNU make&lt;br /&gt;
* gsed -i to perform inline replacements&lt;br /&gt;
* gwatch (/usr/sbin/watch allows the user to examine all data coming through a specified tty, that&#039;s quite different)&lt;br /&gt;
&lt;br /&gt;
Note grep is the BSD one, but documented to be GNU compatible.&lt;br /&gt;
&lt;br /&gt;
=== Directories ===&lt;br /&gt;
* Use your /home/luser directory for anything personal&lt;br /&gt;
* Use /srv or /var when you need to share something, that generally requires a Salt config to give shared rights to that directory&lt;br /&gt;
* /var/51-wwwroot on Ysul is to use instead of your home folder for staging area of web applications you want to serve&lt;br /&gt;
* /datacube contains separate ZFS filesystems for a chunk of data, for example a backup, a collection of Git repositories, they can be declared in {{Ops file|pillar/devserver/datacubes.sls}}&lt;br /&gt;
&lt;br /&gt;
=== tmux ===&lt;br /&gt;
* You can use &amp;lt;code&amp;gt;reptyr &amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt; to attach a process spawned outside a tmux session in one&lt;br /&gt;
* [https://gist.github.com/MohamedAlaa/2961058 tmux cheat sheet]&lt;br /&gt;
&lt;br /&gt;
=== Mouse support ===&lt;br /&gt;
* nano allows to enable mouse mode with meta+m (ESC, m or alt + m)&lt;br /&gt;
** you can click on any character you want to navigate to&lt;br /&gt;
** if you initiate a selection with shift + up/down, you can extend it with the mouse&lt;br /&gt;
* in tmux, mouse mode (to configure, for example ctrl + b, m) allows you to resize panes&lt;br /&gt;
* mouse scroll (mouse wheel or swipe right of your touchpad) generally works like page up / page down to scroll in a buffer&lt;br /&gt;
&lt;br /&gt;
=== Modern Neovim support ===&lt;br /&gt;
&lt;br /&gt;
The devservers provide a fully provisioned environment for modern Neovim development, in addition to the previous generations vi editors.&lt;br /&gt;
&lt;br /&gt;
You can run &amp;lt;code&amp;gt;vi&amp;lt;/code&amp;gt; (very old nvi, shipped with FreeBSD OS), &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; for traditional Vim or &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; for modern Neovim environment.&lt;br /&gt;
&lt;br /&gt;
We recommend to use &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; and support the [https://www.astronvim.com/ AstroNvim] distribution. All system dependencies are already installed and managed via Salt.&lt;br /&gt;
&lt;br /&gt;
==== AstroNvim ====&lt;br /&gt;
&lt;br /&gt;
To start with AstroNvim:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.config&lt;br /&gt;
git clone https://github.com/AstroNvim/template ~/.config/nvim&lt;br /&gt;
nvim&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the first launch, Neovim will automatically download all required plugins and compile Treesitter parsers. This may take a minute. Once finished, you can type &amp;lt;code&amp;gt;:qa&amp;lt;/code&amp;gt; to exit and relaunch.&lt;br /&gt;
&lt;br /&gt;
It&#039;s now ready to use. To add support for a specific language, type &amp;lt;code&amp;gt;:Mason&amp;lt;/code&amp;gt; and search for the language server relevant to your development.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to install a [https://www.nerdfonts.com/ Nerd Font] in your own OS to see the symbols, as a lot of modern Neovim plugins rely on them.&lt;br /&gt;
&lt;br /&gt;
==== Other frameworks ====&lt;br /&gt;
&lt;br /&gt;
LazyNvim and other configuration frameworks built upon lazy.nvim should work too.&lt;br /&gt;
&lt;br /&gt;
==== Basic vim confguration ====&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want an IDE vibe but a lightweight editor, you can also benefit from  Amir Salihefendic settings.&lt;br /&gt;
&lt;br /&gt;
Use https://github.com/amix/vimrc/blob/master/vimrcs/basic.vim as your &amp;lt;code&amp;gt;$HOME/.vimrc&amp;lt;/code&amp;gt; file, then run &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Customize themes ===&lt;br /&gt;
==== Save configuration files ====&lt;br /&gt;
&lt;br /&gt;
You can commit directly in the Operations repository your own dotfiles to configure your applications themes, or any other settings.&lt;br /&gt;
&lt;br /&gt;
Create a directory with your username at the following location:&lt;br /&gt;
* for every server: {{Ops file|roles/core/userland-home/files}}&lt;br /&gt;
* for devserver only: {{Ops file|roles/devserver/userland-home/files}}&lt;br /&gt;
&lt;br /&gt;
Content from those directories will be directly deployed in your home directory.&lt;br /&gt;
&lt;br /&gt;
==== Nerd Font and Powerline ====&lt;br /&gt;
&lt;br /&gt;
SSH server accepts the POWERLINE_SUPPORTED environment variable, so you can add in your own client shell configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
export POWERLINE_SUPPORTED=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then on the server, you can read it to apply a different theme for your prompt, tmux, etc. when you&#039;ve this variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
if [[ -n &amp;quot;$POWERLINE_SUPPORTED&amp;quot; ]]; then&lt;br /&gt;
    source ~/.config/zsh/powerline-prompt.zsh&lt;br /&gt;
else&lt;br /&gt;
    source ~/.config/zsh/fallback-prompt.zsh&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We&#039;re open to other ideas to improve better integration with client themes.&lt;br /&gt;
If you save any setting in Operations repository, you can attach {{T|2361}} as a reference.&lt;br /&gt;
&lt;br /&gt;
== FreeBSD development ==&lt;br /&gt;
&lt;br /&gt;
For FreeBSD development tasks, WindRiver could be suitable if you need a amd64 arch.&lt;br /&gt;
It&#039;s certainly suitable for ports development, as Poudriere is installed there.&lt;br /&gt;
&lt;br /&gt;
=== Ports ===&lt;br /&gt;
==== Poudriere ====&lt;br /&gt;
The [https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere FreeBSD porter handbook suggests Poudriere] as a convenient way to test ports in a clean environment.&lt;br /&gt;
&lt;br /&gt;
One of the big interest of this method is to start from scratch, allowing to detect hidden dependencies, like &amp;lt;code&amp;gt;pkg-config&amp;lt;/code&amp;gt; silently invoked by autotools.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Once time setup.&#039;&#039;&#039; Poudriere is installed in devserver role. To use it you need:&lt;br /&gt;
  - a FreeBSD ports tree, let&#039;s say you clone it to /home/luser/dev/freebsd/ports&lt;br /&gt;
  - to declare `luser-dev: /home/luser/dev/freebsd/ports` to {{Ops file|pillar/devserver/ports.sls}} under poudriere.ports&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Build a port.&#039;&#039;&#039; Then you can work in /home/luser/dev/freebsd/ports and build through:&lt;br /&gt;
&lt;br /&gt;
    sudo poudriere testport -j base14 -p luser_dev -o somecategory/someport&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Logs.&#039;&#039;&#039; You can browse logs on the web, as https://windriver.nasqueron.org/poudriere serves &amp;lt;code&amp;gt;/usr/local/poudriere/data/logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache.&#039;&#039;&#039; To speed up recompilation, [https://ccache.dev/ ccache] is used as disk cache. Metrics for the cache are available for WindRiver at http://172.27.27.35:2337/metrics/ccache (curl it from the server).&lt;br /&gt;
&lt;br /&gt;
== Databases ==&lt;br /&gt;
&lt;br /&gt;
=== MariaDB ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-mysql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
You&#039;ve access to test_% databases, so you can without any specific configuration use that for unit tests. It&#039;s even OK to connect without login or password for the test_% namespace.&lt;br /&gt;
&lt;br /&gt;
If you need any other database and app config, you can request it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20MariaDB%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
For long term projects, you can also specify where you want the credentials to be be provisioned,&lt;br /&gt;
or request a Vault AppRole for your application to fetch them securely.&lt;br /&gt;
&lt;br /&gt;
=== PostgreSQL ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-pgsql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20PostgreSQL%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Redis ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;For licensing purpose, should be replaced by Valkey or upgraded to a recent version under AGPLv3. See [https://devcentral.nasqueron.org/T1938 this discussion on DevCentral] for more context.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Redis is available on WindRiver, with database 0 used by NetBox.&lt;br /&gt;
&lt;br /&gt;
== General build information ==&lt;br /&gt;
=== Number of CPUS ===&lt;br /&gt;
On FreeBSD, the number of CPU core can be given by &amp;lt;code&amp;gt;sysctl hw.ncpu&amp;lt;/code&amp;gt; while the full XML description is available at &amp;lt;code&amp;gt;sysctl kern.sched.topology_spec&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Modern CPU allow for parallel compile tasks, use that.&lt;br /&gt;
&lt;br /&gt;
For example, as of October 2024, you can use 12 core to build on WindRiver.&lt;br /&gt;
&lt;br /&gt;
=== ccache ===&lt;br /&gt;
A compiler cache is installed with ccache.&lt;br /&gt;
&lt;br /&gt;
The /etc/make.conf configuration file defines /var/cache/ccache directory to be used for ports build.&lt;br /&gt;
That directory can&#039;t only be written as root.&lt;br /&gt;
&lt;br /&gt;
You can create your own configuration in &amp;lt;code&amp;gt;$HOME/.ccache/ccache.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
The exact path and configuration values can be get by &amp;lt;code&amp;gt;ccache -s&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Rust ==&lt;br /&gt;
=== Rustup ===&lt;br /&gt;
If you wish to have control of what stable and nightly Rust versions to use, you can use Rustup.&lt;br /&gt;
&lt;br /&gt;
To use it, the command &amp;lt;code&amp;gt;rustup-init&amp;lt;/code&amp;gt; is available as an alternative to the curl magic documented on the Rustup web site (actually it&#039;s a fairly recent version of that script saved locally).&lt;br /&gt;
&lt;br /&gt;
=== cargo, rustc ===&lt;br /&gt;
If you&#039;re OK with the Rust version installed by OS packages, feel free to call directly cargo or rustc.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
You can generate project documentation with the `cargo doc` command.&lt;br /&gt;
&lt;br /&gt;
== PHP ==&lt;br /&gt;
=== Tools provided ===&lt;br /&gt;
The following software are maintained as phar or repository clones in /opt:&lt;br /&gt;
* psysh, REPL shell to test PHP code (with PHP documentation available at /usr/local/share/psysh/php_manual.sqlite)&lt;br /&gt;
* psalm, for static analysis&lt;br /&gt;
* doctum and phpdoc (phpDocumentor) to generate documentation&lt;br /&gt;
* phpunit&lt;br /&gt;
* phpcs&lt;br /&gt;
&lt;br /&gt;
Most of those tools have wrappers in /usr/local/bin trying to detect if a version is installed locally for the PHP project through Composer and switching to that version if available.&lt;br /&gt;
&lt;br /&gt;
So by running &amp;quot;phpunit&amp;quot;, you&#039;ll run your Composer version and the system version when missing.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
&lt;br /&gt;
To generate documentation for your code, &amp;lt;code&amp;gt;phpdoc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;doctum&amp;lt;/code&amp;gt; commands are available, including GraphViz and PlantUML for phpDocumentor classes graphes.&lt;br /&gt;
&lt;br /&gt;
== Network use ==&lt;br /&gt;
=== Port to use to spawn a server ===&lt;br /&gt;
To spawn a temporary web server, you can use ports 1080, 8000 or 8080 for a recommended 30 minutes maximum duration.&lt;br /&gt;
&lt;br /&gt;
For longer temporary need, the ports 3000, 3100, 3300, 3400, 3500, 3600, 3700, 3800 and 3900 are recommended.&lt;br /&gt;
&lt;br /&gt;
Other applications server can use any free port. Any long-term server should add an entry into {{Ops file|PORTS}}.&lt;br /&gt;
&lt;br /&gt;
== SSH tunnels ==&lt;br /&gt;
You can connect to the devserver and create one or more tunnels where you need:&lt;br /&gt;
* directly on the devserver: &amp;lt;code&amp;gt;ssh -L 3306:localhost:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
* to another server: &amp;lt;code&amp;gt;ssh -L 3307:db-B-001:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Format is always -L &amp;lt;port on your computer&amp;gt;:&amp;lt;server ip or hostname&amp;gt;:&amp;lt;port on the server&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resources are reachable on your computer going to localhost and the port you specified locally (e.g. localhost:3307 for db-B-001 MariaDB).&lt;br /&gt;
&lt;br /&gt;
=== Certificates for private network resources ===&lt;br /&gt;
Resources using TLS connections on the private network use certificates managed by the CA we&#039;ve set up with Vault.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to install on your computer the root certificate: {{Ops file|roles/core/certificates/files/nasqueron-vault-ca.crt}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - Linux and UNIX&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Step 1. Install package !! Step 2. Drop .crt in directory !! Step 3. Run update command&lt;br /&gt;
|-&lt;br /&gt;
| Arch Linux || ca-certificates-utils || /etc/ca-certificates/trust-source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| Debian/Ubuntu|| ca-certificates || /usr/local/share/ca-certificates || update-ca-certificates&lt;br /&gt;
|-&lt;br /&gt;
| Fedora/RHEL || ca-certificates || /etc/pki/ca-trust/source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| FreeBSD || ca_root_nss || /usr/local/share/certs || &#039;&#039;You&#039;re done, nothing to run :)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| openSUSE || ca-certificates-mozilla || /etc/pki/trust/anchors || update-ca-certificates&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - OS using GUI&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Procedure&lt;br /&gt;
|-&lt;br /&gt;
| ChromeOS || Chrome browser → Settings → Privacy and security → Security → Manage certificates → Authorities → Import&lt;br /&gt;
|-&lt;br /&gt;
| macOS || Keychain Access → System keychain → File &amp;gt; Import Items…&lt;br /&gt;
|-&lt;br /&gt;
| Windows || Import into “Trusted Root Certification Authorities” (mmc → Certificates snap-in)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Acceptable use policy ==&lt;br /&gt;
&amp;lt;poem&amp;gt;&lt;br /&gt;
&#039;&#039;The devserver is a shared space built on trust.&#039;&#039;&lt;br /&gt;
&#039;&#039;It exists to enable creativity, learning, and collaboration, not to enforce restrictive usage rules.&#039;&#039;&lt;br /&gt;
&amp;lt;/poem&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The devserver is primarily intended to:&lt;br /&gt;
&lt;br /&gt;
* support any activity related to Nasqueron;&lt;br /&gt;
* support contributions to open source software and free culture in general;&lt;br /&gt;
* support participation in technical workshops organized with Nasqueron.&lt;br /&gt;
&lt;br /&gt;
Any activity falling within these purposes is encouraged.&lt;br /&gt;
&lt;br /&gt;
The devserver may also be used to:&lt;br /&gt;
&lt;br /&gt;
* host personal content, provided resource usage remains reasonable (disk space, CPU, RAM, bandwidth);&lt;br /&gt;
* support activities aimed at learning, experimentation, and knowledge sharing.&lt;br /&gt;
&lt;br /&gt;
There is no requirement for all hosted content to be open source. Closed-source software, personal documents, or non-public projects are acceptable as long as they do not interfere with the normal operation of the service or other users.&lt;br /&gt;
&lt;br /&gt;
IRC usage should normally take place on Eglide.&lt;br /&gt;
However, hosting a lightweight IRC client (such as irssi or weechat) on another devserver, for example to avoid managing multiple SSH or tmux sessions, is acceptable.&lt;br /&gt;
&lt;br /&gt;
== Personal notes ==&lt;br /&gt;
&lt;br /&gt;
You can add in this section links to your own personal notes to use the devserver for your activities, including ideally a disaster recovery plan (how do you reinstall the code your work with?)&lt;br /&gt;
&lt;br /&gt;
* [[User:Dereckson/Devserver]]&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== You&#039;ve lost a file ===&lt;br /&gt;
&lt;br /&gt;
Your home directory has snapshots every 5 minutes, you can get back your recently lost file from a read-only snapshot:&lt;br /&gt;
&lt;br /&gt;
    cd ~/.zfs/snapshot&lt;br /&gt;
    ls&lt;br /&gt;
&lt;br /&gt;
Those are folders with your home directory content as it was at the snapshot time, you can even diff files to spot differences:&lt;br /&gt;
&lt;br /&gt;
    cd zfs-auto-snap_hourly-2023-06-05-20h00&lt;br /&gt;
    diff -u dev/nasqueron/operations/alkane/Jenkinsfile ~/dev/nasqueron/operations/alkane/Jenkinsfile&lt;br /&gt;
&lt;br /&gt;
You&#039;ve 12 months to act if your file is already in a monthly snapshot, but only 2 hours if it&#039;s in the last frequent snapshot and you keep modifying files, so act as quick as possible, ie right now. If you can&#039;t act right now, at least save the snapshot having the file (check that to avoid to save the wrong one) you want to recover with &amp;lt;code&amp;gt;zfs hold&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    zfs hold keep arcology/usr/home/dereckson@zfs-auto-snap_hourly-2023-06-05-22h00&lt;br /&gt;
&lt;br /&gt;
=== SSH issues ===&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve a message like &amp;lt;code&amp;gt;username@windriver.nasqueron.org: Permission denied (publickey).&amp;lt;/code&amp;gt;, it can means:&lt;br /&gt;
&lt;br /&gt;
* No SSH key has been offered&lt;br /&gt;
* SSH keys have been offered, but not recognized by the server&lt;br /&gt;
** Check {{Ops file|pillar/core/users.sls}} - do you have your public key on it?&lt;br /&gt;
** If not, and you have a new key, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add your new SSH key]&lt;br /&gt;
* SSH key is in a non-standard path - launch a SSH agent if needed (you&#039;ve already one with your desktop engine), then ssh-add &amp;lt;path to key&amp;gt;&lt;br /&gt;
* SSH key is protected by a passphrase and you don&#039;t remember the password&lt;br /&gt;
** Solution 1. Try to remember the password, with an agent and ssh-add, it will be less confusing as you&#039;ll work locally&lt;br /&gt;
** Solution 2. Destroy the SSH key, create a new one, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add that new key], login or ssh-add each day for one week to remember it&lt;br /&gt;
&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:PHP]]&lt;br /&gt;
[[Category:ZFS]]&lt;br /&gt;
[[Category:Contributor guide]]&lt;br /&gt;
[[Category:Reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2607</id>
		<title>Devserver reference</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2607"/>
		<updated>2026-09-13T11:24:40Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Modern Neovim support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nasqueron Operations SIG maintains &#039;&#039;&#039;devservers&#039;&#039;&#039;. They offer a remote development environment for Nasqueron and open source projects.&lt;br /&gt;
&lt;br /&gt;
== General information ==&lt;br /&gt;
=== What server to use? ===&lt;br /&gt;
&lt;br /&gt;
* WindRiver, for any general-purpose task&lt;br /&gt;
* Ysul, for webserver-legacy sites&lt;br /&gt;
* Dwellers, for Docker development&lt;br /&gt;
* Eglide, if you need an IRC session&lt;br /&gt;
&lt;br /&gt;
=== How to get access? ===&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/3/&lt;br /&gt;
  | text = Request account&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
You can ask it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
==== Do it yourself ====&lt;br /&gt;
&lt;br /&gt;
You&#039;re encouraged to submit a change with the configuration for your own access. That helps to speed up the process and ensure the right information is there:&lt;br /&gt;
&lt;br /&gt;
* Familiar with Git and our contributions workflow? follow NOG instructions at [[Operations grimoire/Create and revoke user accounts on Salt servers]] and send a commit to DevCentral against our operations repository.&lt;br /&gt;
* Newcomer ? We also have a full guide in French explaining step by step how to create that change on a Windows system and send it to review: [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Groups to use&lt;br /&gt;
|-&lt;br /&gt;
! Server !! Group name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Ysul / WindRiver || nasquenautes || for general purpose development&lt;br /&gt;
|-&lt;br /&gt;
| Dwellers || nasqueron-dev-docker || for Docker development&lt;br /&gt;
|-&lt;br /&gt;
| Eglide || shell || for IRC purpose&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Userland ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Normally, devservers run under FreeBSD. That means your userland is a BSD one, and some commands arguments can be different if you&#039;re used to Linux. GNU coretools utilities are installed and prefixed by the letter g, e.g. gmake or gsed.&lt;br /&gt;
&lt;br /&gt;
The most useful GNU commands are:&lt;br /&gt;
* gmake when the Makefile has been written for GNU make&lt;br /&gt;
* gsed -i to perform inline replacements&lt;br /&gt;
* gwatch (/usr/sbin/watch allows the user to examine all data coming through a specified tty, that&#039;s quite different)&lt;br /&gt;
&lt;br /&gt;
Note grep is the BSD one, but documented to be GNU compatible.&lt;br /&gt;
&lt;br /&gt;
=== Directories ===&lt;br /&gt;
* Use your /home/luser directory for anything personal&lt;br /&gt;
* Use /srv or /var when you need to share something, that generally requires a Salt config to give shared rights to that directory&lt;br /&gt;
* /var/51-wwwroot on Ysul is to use instead of your home folder for staging area of web applications you want to serve&lt;br /&gt;
* /datacube contains separate ZFS filesystems for a chunk of data, for example a backup, a collection of Git repositories, they can be declared in {{Ops file|pillar/devserver/datacubes.sls}}&lt;br /&gt;
&lt;br /&gt;
=== tmux ===&lt;br /&gt;
* You can use &amp;lt;code&amp;gt;reptyr &amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt; to attach a process spawned outside a tmux session in one&lt;br /&gt;
* [https://gist.github.com/MohamedAlaa/2961058 tmux cheat sheet]&lt;br /&gt;
&lt;br /&gt;
=== Mouse support ===&lt;br /&gt;
* nano allows to enable mouse mode with meta+m (ESC, m or alt + m)&lt;br /&gt;
** you can click on any character you want to navigate to&lt;br /&gt;
** if you initiate a selection with shift + up/down, you can extend it with the mouse&lt;br /&gt;
* in tmux, mouse mode (to configure, for example ctrl + b, m) allows you to resize panes&lt;br /&gt;
* mouse scroll (mouse wheel or swipe right of your touchpad) generally works like page up / page down to scroll in a buffer&lt;br /&gt;
&lt;br /&gt;
=== Modern Neovim support ===&lt;br /&gt;
&lt;br /&gt;
The devservers provide a fully provisioned environment for modern Neovim development, in addition to the previous generations vi editors.&lt;br /&gt;
&lt;br /&gt;
You can run &amp;lt;code&amp;gt;vi&amp;lt;/code&amp;gt; (very old nvi, shipped with FreeBSD OS), &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; for traditional Vim or &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; for modern Neovim environment.&lt;br /&gt;
&lt;br /&gt;
We recommend to use &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; and support the [https://www.astronvim.com/ AstroNvim] distribution. All system dependencies are already installed and managed via Salt.&lt;br /&gt;
&lt;br /&gt;
==== AstroNvim ====&lt;br /&gt;
&lt;br /&gt;
To start with AstroNvim:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.config&lt;br /&gt;
git clone https://github.com/AstroNvim/template ~/.config/nvim&lt;br /&gt;
nvim&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the first launch, Neovim will automatically download all required plugins and compile Treesitter parsers. This may take a minute. Once finished, you can type &amp;lt;code&amp;gt;:qa&amp;lt;/code&amp;gt; to exit and relaunch.&lt;br /&gt;
&lt;br /&gt;
It&#039;s now ready to use. To add support for a specific language, type &amp;lt;code&amp;gt;:Mason&amp;lt;/code&amp;gt; and search for the language server relevant to your development.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to install a [https://www.nerdfonts.com/ Nerd Font] in your own OS to see the symbols, as a lot of modern Neovim plugins rely on them.&lt;br /&gt;
&lt;br /&gt;
==== Other frameworks ====&lt;br /&gt;
&lt;br /&gt;
LazyNvim and other configuration frameworks built upon lazy.nvim should work too.&lt;br /&gt;
&lt;br /&gt;
=== Customize themes ===&lt;br /&gt;
==== Save configuration files ====&lt;br /&gt;
&lt;br /&gt;
You can commit directly in the Operations repository your own dotfiles to configure your applications themes, or any other settings.&lt;br /&gt;
&lt;br /&gt;
Create a directory with your username at the following location:&lt;br /&gt;
* for every server: {{Ops file|roles/core/userland-home/files}}&lt;br /&gt;
* for devserver only: {{Ops file|roles/devserver/userland-home/files}}&lt;br /&gt;
&lt;br /&gt;
Content from those directories will be directly deployed in your home directory.&lt;br /&gt;
&lt;br /&gt;
==== Nerd Font and Powerline ====&lt;br /&gt;
&lt;br /&gt;
SSH server accepts the POWERLINE_SUPPORTED environment variable, so you can add in your own client shell configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
export POWERLINE_SUPPORTED=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then on the server, you can read it to apply a different theme for your prompt, tmux, etc. when you&#039;ve this variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
if [[ -n &amp;quot;$POWERLINE_SUPPORTED&amp;quot; ]]; then&lt;br /&gt;
    source ~/.config/zsh/powerline-prompt.zsh&lt;br /&gt;
else&lt;br /&gt;
    source ~/.config/zsh/fallback-prompt.zsh&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We&#039;re open to other ideas to improve better integration with client themes.&lt;br /&gt;
If you save any setting in Operations repository, you can attach {{T|2361}} as a reference.&lt;br /&gt;
&lt;br /&gt;
== FreeBSD development ==&lt;br /&gt;
&lt;br /&gt;
For FreeBSD development tasks, WindRiver could be suitable if you need a amd64 arch.&lt;br /&gt;
It&#039;s certainly suitable for ports development, as Poudriere is installed there.&lt;br /&gt;
&lt;br /&gt;
=== Ports ===&lt;br /&gt;
==== Poudriere ====&lt;br /&gt;
The [https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere FreeBSD porter handbook suggests Poudriere] as a convenient way to test ports in a clean environment.&lt;br /&gt;
&lt;br /&gt;
One of the big interest of this method is to start from scratch, allowing to detect hidden dependencies, like &amp;lt;code&amp;gt;pkg-config&amp;lt;/code&amp;gt; silently invoked by autotools.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Once time setup.&#039;&#039;&#039; Poudriere is installed in devserver role. To use it you need:&lt;br /&gt;
  - a FreeBSD ports tree, let&#039;s say you clone it to /home/luser/dev/freebsd/ports&lt;br /&gt;
  - to declare `luser-dev: /home/luser/dev/freebsd/ports` to {{Ops file|pillar/devserver/ports.sls}} under poudriere.ports&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Build a port.&#039;&#039;&#039; Then you can work in /home/luser/dev/freebsd/ports and build through:&lt;br /&gt;
&lt;br /&gt;
    sudo poudriere testport -j base14 -p luser_dev -o somecategory/someport&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Logs.&#039;&#039;&#039; You can browse logs on the web, as https://windriver.nasqueron.org/poudriere serves &amp;lt;code&amp;gt;/usr/local/poudriere/data/logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache.&#039;&#039;&#039; To speed up recompilation, [https://ccache.dev/ ccache] is used as disk cache. Metrics for the cache are available for WindRiver at http://172.27.27.35:2337/metrics/ccache (curl it from the server).&lt;br /&gt;
&lt;br /&gt;
== Databases ==&lt;br /&gt;
&lt;br /&gt;
=== MariaDB ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-mysql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
You&#039;ve access to test_% databases, so you can without any specific configuration use that for unit tests. It&#039;s even OK to connect without login or password for the test_% namespace.&lt;br /&gt;
&lt;br /&gt;
If you need any other database and app config, you can request it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20MariaDB%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
For long term projects, you can also specify where you want the credentials to be be provisioned,&lt;br /&gt;
or request a Vault AppRole for your application to fetch them securely.&lt;br /&gt;
&lt;br /&gt;
=== PostgreSQL ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-pgsql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20PostgreSQL%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Redis ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;For licensing purpose, should be replaced by Valkey or upgraded to a recent version under AGPLv3. See [https://devcentral.nasqueron.org/T1938 this discussion on DevCentral] for more context.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Redis is available on WindRiver, with database 0 used by NetBox.&lt;br /&gt;
&lt;br /&gt;
== General build information ==&lt;br /&gt;
=== Number of CPUS ===&lt;br /&gt;
On FreeBSD, the number of CPU core can be given by &amp;lt;code&amp;gt;sysctl hw.ncpu&amp;lt;/code&amp;gt; while the full XML description is available at &amp;lt;code&amp;gt;sysctl kern.sched.topology_spec&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Modern CPU allow for parallel compile tasks, use that.&lt;br /&gt;
&lt;br /&gt;
For example, as of October 2024, you can use 12 core to build on WindRiver.&lt;br /&gt;
&lt;br /&gt;
=== ccache ===&lt;br /&gt;
A compiler cache is installed with ccache.&lt;br /&gt;
&lt;br /&gt;
The /etc/make.conf configuration file defines /var/cache/ccache directory to be used for ports build.&lt;br /&gt;
That directory can&#039;t only be written as root.&lt;br /&gt;
&lt;br /&gt;
You can create your own configuration in &amp;lt;code&amp;gt;$HOME/.ccache/ccache.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
The exact path and configuration values can be get by &amp;lt;code&amp;gt;ccache -s&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Rust ==&lt;br /&gt;
=== Rustup ===&lt;br /&gt;
If you wish to have control of what stable and nightly Rust versions to use, you can use Rustup.&lt;br /&gt;
&lt;br /&gt;
To use it, the command &amp;lt;code&amp;gt;rustup-init&amp;lt;/code&amp;gt; is available as an alternative to the curl magic documented on the Rustup web site (actually it&#039;s a fairly recent version of that script saved locally).&lt;br /&gt;
&lt;br /&gt;
=== cargo, rustc ===&lt;br /&gt;
If you&#039;re OK with the Rust version installed by OS packages, feel free to call directly cargo or rustc.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
You can generate project documentation with the `cargo doc` command.&lt;br /&gt;
&lt;br /&gt;
== PHP ==&lt;br /&gt;
=== Tools provided ===&lt;br /&gt;
The following software are maintained as phar or repository clones in /opt:&lt;br /&gt;
* psysh, REPL shell to test PHP code (with PHP documentation available at /usr/local/share/psysh/php_manual.sqlite)&lt;br /&gt;
* psalm, for static analysis&lt;br /&gt;
* doctum and phpdoc (phpDocumentor) to generate documentation&lt;br /&gt;
* phpunit&lt;br /&gt;
* phpcs&lt;br /&gt;
&lt;br /&gt;
Most of those tools have wrappers in /usr/local/bin trying to detect if a version is installed locally for the PHP project through Composer and switching to that version if available.&lt;br /&gt;
&lt;br /&gt;
So by running &amp;quot;phpunit&amp;quot;, you&#039;ll run your Composer version and the system version when missing.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
&lt;br /&gt;
To generate documentation for your code, &amp;lt;code&amp;gt;phpdoc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;doctum&amp;lt;/code&amp;gt; commands are available, including GraphViz and PlantUML for phpDocumentor classes graphes.&lt;br /&gt;
&lt;br /&gt;
== Network use ==&lt;br /&gt;
=== Port to use to spawn a server ===&lt;br /&gt;
To spawn a temporary web server, you can use ports 1080, 8000 or 8080 for a recommended 30 minutes maximum duration.&lt;br /&gt;
&lt;br /&gt;
For longer temporary need, the ports 3000, 3100, 3300, 3400, 3500, 3600, 3700, 3800 and 3900 are recommended.&lt;br /&gt;
&lt;br /&gt;
Other applications server can use any free port. Any long-term server should add an entry into {{Ops file|PORTS}}.&lt;br /&gt;
&lt;br /&gt;
== SSH tunnels ==&lt;br /&gt;
You can connect to the devserver and create one or more tunnels where you need:&lt;br /&gt;
* directly on the devserver: &amp;lt;code&amp;gt;ssh -L 3306:localhost:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
* to another server: &amp;lt;code&amp;gt;ssh -L 3307:db-B-001:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Format is always -L &amp;lt;port on your computer&amp;gt;:&amp;lt;server ip or hostname&amp;gt;:&amp;lt;port on the server&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resources are reachable on your computer going to localhost and the port you specified locally (e.g. localhost:3307 for db-B-001 MariaDB).&lt;br /&gt;
&lt;br /&gt;
=== Certificates for private network resources ===&lt;br /&gt;
Resources using TLS connections on the private network use certificates managed by the CA we&#039;ve set up with Vault.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to install on your computer the root certificate: {{Ops file|roles/core/certificates/files/nasqueron-vault-ca.crt}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - Linux and UNIX&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Step 1. Install package !! Step 2. Drop .crt in directory !! Step 3. Run update command&lt;br /&gt;
|-&lt;br /&gt;
| Arch Linux || ca-certificates-utils || /etc/ca-certificates/trust-source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| Debian/Ubuntu|| ca-certificates || /usr/local/share/ca-certificates || update-ca-certificates&lt;br /&gt;
|-&lt;br /&gt;
| Fedora/RHEL || ca-certificates || /etc/pki/ca-trust/source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| FreeBSD || ca_root_nss || /usr/local/share/certs || &#039;&#039;You&#039;re done, nothing to run :)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| openSUSE || ca-certificates-mozilla || /etc/pki/trust/anchors || update-ca-certificates&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - OS using GUI&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Procedure&lt;br /&gt;
|-&lt;br /&gt;
| ChromeOS || Chrome browser → Settings → Privacy and security → Security → Manage certificates → Authorities → Import&lt;br /&gt;
|-&lt;br /&gt;
| macOS || Keychain Access → System keychain → File &amp;gt; Import Items…&lt;br /&gt;
|-&lt;br /&gt;
| Windows || Import into “Trusted Root Certification Authorities” (mmc → Certificates snap-in)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Acceptable use policy ==&lt;br /&gt;
&amp;lt;poem&amp;gt;&lt;br /&gt;
&#039;&#039;The devserver is a shared space built on trust.&#039;&#039;&lt;br /&gt;
&#039;&#039;It exists to enable creativity, learning, and collaboration, not to enforce restrictive usage rules.&#039;&#039;&lt;br /&gt;
&amp;lt;/poem&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The devserver is primarily intended to:&lt;br /&gt;
&lt;br /&gt;
* support any activity related to Nasqueron;&lt;br /&gt;
* support contributions to open source software and free culture in general;&lt;br /&gt;
* support participation in technical workshops organized with Nasqueron.&lt;br /&gt;
&lt;br /&gt;
Any activity falling within these purposes is encouraged.&lt;br /&gt;
&lt;br /&gt;
The devserver may also be used to:&lt;br /&gt;
&lt;br /&gt;
* host personal content, provided resource usage remains reasonable (disk space, CPU, RAM, bandwidth);&lt;br /&gt;
* support activities aimed at learning, experimentation, and knowledge sharing.&lt;br /&gt;
&lt;br /&gt;
There is no requirement for all hosted content to be open source. Closed-source software, personal documents, or non-public projects are acceptable as long as they do not interfere with the normal operation of the service or other users.&lt;br /&gt;
&lt;br /&gt;
IRC usage should normally take place on Eglide.&lt;br /&gt;
However, hosting a lightweight IRC client (such as irssi or weechat) on another devserver, for example to avoid managing multiple SSH or tmux sessions, is acceptable.&lt;br /&gt;
&lt;br /&gt;
== Personal notes ==&lt;br /&gt;
&lt;br /&gt;
You can add in this section links to your own personal notes to use the devserver for your activities, including ideally a disaster recovery plan (how do you reinstall the code your work with?)&lt;br /&gt;
&lt;br /&gt;
* [[User:Dereckson/Devserver]]&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== You&#039;ve lost a file ===&lt;br /&gt;
&lt;br /&gt;
Your home directory has snapshots every 5 minutes, you can get back your recently lost file from a read-only snapshot:&lt;br /&gt;
&lt;br /&gt;
    cd ~/.zfs/snapshot&lt;br /&gt;
    ls&lt;br /&gt;
&lt;br /&gt;
Those are folders with your home directory content as it was at the snapshot time, you can even diff files to spot differences:&lt;br /&gt;
&lt;br /&gt;
    cd zfs-auto-snap_hourly-2023-06-05-20h00&lt;br /&gt;
    diff -u dev/nasqueron/operations/alkane/Jenkinsfile ~/dev/nasqueron/operations/alkane/Jenkinsfile&lt;br /&gt;
&lt;br /&gt;
You&#039;ve 12 months to act if your file is already in a monthly snapshot, but only 2 hours if it&#039;s in the last frequent snapshot and you keep modifying files, so act as quick as possible, ie right now. If you can&#039;t act right now, at least save the snapshot having the file (check that to avoid to save the wrong one) you want to recover with &amp;lt;code&amp;gt;zfs hold&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    zfs hold keep arcology/usr/home/dereckson@zfs-auto-snap_hourly-2023-06-05-22h00&lt;br /&gt;
&lt;br /&gt;
=== SSH issues ===&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve a message like &amp;lt;code&amp;gt;username@windriver.nasqueron.org: Permission denied (publickey).&amp;lt;/code&amp;gt;, it can means:&lt;br /&gt;
&lt;br /&gt;
* No SSH key has been offered&lt;br /&gt;
* SSH keys have been offered, but not recognized by the server&lt;br /&gt;
** Check {{Ops file|pillar/core/users.sls}} - do you have your public key on it?&lt;br /&gt;
** If not, and you have a new key, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add your new SSH key]&lt;br /&gt;
* SSH key is in a non-standard path - launch a SSH agent if needed (you&#039;ve already one with your desktop engine), then ssh-add &amp;lt;path to key&amp;gt;&lt;br /&gt;
* SSH key is protected by a passphrase and you don&#039;t remember the password&lt;br /&gt;
** Solution 1. Try to remember the password, with an agent and ssh-add, it will be less confusing as you&#039;ll work locally&lt;br /&gt;
** Solution 2. Destroy the SSH key, create a new one, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add that new key], login or ssh-add each day for one week to remember it&lt;br /&gt;
&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:PHP]]&lt;br /&gt;
[[Category:ZFS]]&lt;br /&gt;
[[Category:Contributor guide]]&lt;br /&gt;
[[Category:Reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2606</id>
		<title>Devserver reference</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2606"/>
		<updated>2026-09-13T11:20:26Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Modern Neovim support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nasqueron Operations SIG maintains &#039;&#039;&#039;devservers&#039;&#039;&#039;. They offer a remote development environment for Nasqueron and open source projects.&lt;br /&gt;
&lt;br /&gt;
== General information ==&lt;br /&gt;
=== What server to use? ===&lt;br /&gt;
&lt;br /&gt;
* WindRiver, for any general-purpose task&lt;br /&gt;
* Ysul, for webserver-legacy sites&lt;br /&gt;
* Dwellers, for Docker development&lt;br /&gt;
* Eglide, if you need an IRC session&lt;br /&gt;
&lt;br /&gt;
=== How to get access? ===&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/3/&lt;br /&gt;
  | text = Request account&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
You can ask it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
==== Do it yourself ====&lt;br /&gt;
&lt;br /&gt;
You&#039;re encouraged to submit a change with the configuration for your own access. That helps to speed up the process and ensure the right information is there:&lt;br /&gt;
&lt;br /&gt;
* Familiar with Git and our contributions workflow? follow NOG instructions at [[Operations grimoire/Create and revoke user accounts on Salt servers]] and send a commit to DevCentral against our operations repository.&lt;br /&gt;
* Newcomer ? We also have a full guide in French explaining step by step how to create that change on a Windows system and send it to review: [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Groups to use&lt;br /&gt;
|-&lt;br /&gt;
! Server !! Group name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Ysul / WindRiver || nasquenautes || for general purpose development&lt;br /&gt;
|-&lt;br /&gt;
| Dwellers || nasqueron-dev-docker || for Docker development&lt;br /&gt;
|-&lt;br /&gt;
| Eglide || shell || for IRC purpose&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Userland ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Normally, devservers run under FreeBSD. That means your userland is a BSD one, and some commands arguments can be different if you&#039;re used to Linux. GNU coretools utilities are installed and prefixed by the letter g, e.g. gmake or gsed.&lt;br /&gt;
&lt;br /&gt;
The most useful GNU commands are:&lt;br /&gt;
* gmake when the Makefile has been written for GNU make&lt;br /&gt;
* gsed -i to perform inline replacements&lt;br /&gt;
* gwatch (/usr/sbin/watch allows the user to examine all data coming through a specified tty, that&#039;s quite different)&lt;br /&gt;
&lt;br /&gt;
Note grep is the BSD one, but documented to be GNU compatible.&lt;br /&gt;
&lt;br /&gt;
=== Directories ===&lt;br /&gt;
* Use your /home/luser directory for anything personal&lt;br /&gt;
* Use /srv or /var when you need to share something, that generally requires a Salt config to give shared rights to that directory&lt;br /&gt;
* /var/51-wwwroot on Ysul is to use instead of your home folder for staging area of web applications you want to serve&lt;br /&gt;
* /datacube contains separate ZFS filesystems for a chunk of data, for example a backup, a collection of Git repositories, they can be declared in {{Ops file|pillar/devserver/datacubes.sls}}&lt;br /&gt;
&lt;br /&gt;
=== tmux ===&lt;br /&gt;
* You can use &amp;lt;code&amp;gt;reptyr &amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt; to attach a process spawned outside a tmux session in one&lt;br /&gt;
* [https://gist.github.com/MohamedAlaa/2961058 tmux cheat sheet]&lt;br /&gt;
&lt;br /&gt;
=== Mouse support ===&lt;br /&gt;
* nano allows to enable mouse mode with meta+m (ESC, m or alt + m)&lt;br /&gt;
** you can click on any character you want to navigate to&lt;br /&gt;
** if you initiate a selection with shift + up/down, you can extend it with the mouse&lt;br /&gt;
* in tmux, mouse mode (to configure, for example ctrl + b, m) allows you to resize panes&lt;br /&gt;
* mouse scroll (mouse wheel or swipe right of your touchpad) generally works like page up / page down to scroll in a buffer&lt;br /&gt;
&lt;br /&gt;
=== Modern Neovim support ===&lt;br /&gt;
&lt;br /&gt;
You can run &amp;lt;code&amp;gt;vi&amp;lt;/code&amp;gt; (very old nvi, shipped with FreeBSD OS), &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; for traditional Vim or &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; for modern Neovim environment.&lt;br /&gt;
&lt;br /&gt;
The devservers provide a fully provisioned environment for modern Neovim development. All system dependencies required by  (including &amp;lt;code&amp;gt;neovim&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ripgrep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;node&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;npm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tree-sitter-cli&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;gmake&amp;lt;/code&amp;gt; for FreeBSD)&lt;br /&gt;
&lt;br /&gt;
We recommend to use &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; and support the [https://www.astronvim.com/ AstroNvim] distribution. All system dependencies are already installed and managed via Salt.&lt;br /&gt;
&lt;br /&gt;
==== AstroNvim ====&lt;br /&gt;
&lt;br /&gt;
To start with AstroNvim:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.config&lt;br /&gt;
git clone https://github.com/AstroNvim/template ~/.config/nvim&lt;br /&gt;
nvim&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the first launch, Neovim will automatically download all required plugins and compile Treesitter parsers. This may take a minute. Once finished, you can type &amp;lt;code&amp;gt;:qa&amp;lt;/code&amp;gt; to exit and relaunch.&lt;br /&gt;
&lt;br /&gt;
It&#039;s now ready to use. To add support for a specific language, type &amp;lt;code&amp;gt;:Mason&amp;lt;/code&amp;gt; and search for the language server relevant to your development.&lt;br /&gt;
&lt;br /&gt;
==== Other frameworks ====&lt;br /&gt;
&lt;br /&gt;
LazyNvim and other configuration frameworks built upon lazy.nvim should work too.&lt;br /&gt;
&lt;br /&gt;
=== Customize themes ===&lt;br /&gt;
==== Save configuration files ====&lt;br /&gt;
&lt;br /&gt;
You can commit directly in the Operations repository your own dotfiles to configure your applications themes, or any other settings.&lt;br /&gt;
&lt;br /&gt;
Create a directory with your username at the following location:&lt;br /&gt;
* for every server: {{Ops file|roles/core/userland-home/files}}&lt;br /&gt;
* for devserver only: {{Ops file|roles/devserver/userland-home/files}}&lt;br /&gt;
&lt;br /&gt;
Content from those directories will be directly deployed in your home directory.&lt;br /&gt;
&lt;br /&gt;
==== Nerd Font and Powerline ====&lt;br /&gt;
&lt;br /&gt;
SSH server accepts the POWERLINE_SUPPORTED environment variable, so you can add in your own client shell configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
export POWERLINE_SUPPORTED=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then on the server, you can read it to apply a different theme for your prompt, tmux, etc. when you&#039;ve this variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
if [[ -n &amp;quot;$POWERLINE_SUPPORTED&amp;quot; ]]; then&lt;br /&gt;
    source ~/.config/zsh/powerline-prompt.zsh&lt;br /&gt;
else&lt;br /&gt;
    source ~/.config/zsh/fallback-prompt.zsh&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We&#039;re open to other ideas to improve better integration with client themes.&lt;br /&gt;
If you save any setting in Operations repository, you can attach {{T|2361}} as a reference.&lt;br /&gt;
&lt;br /&gt;
== FreeBSD development ==&lt;br /&gt;
&lt;br /&gt;
For FreeBSD development tasks, WindRiver could be suitable if you need a amd64 arch.&lt;br /&gt;
It&#039;s certainly suitable for ports development, as Poudriere is installed there.&lt;br /&gt;
&lt;br /&gt;
=== Ports ===&lt;br /&gt;
==== Poudriere ====&lt;br /&gt;
The [https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere FreeBSD porter handbook suggests Poudriere] as a convenient way to test ports in a clean environment.&lt;br /&gt;
&lt;br /&gt;
One of the big interest of this method is to start from scratch, allowing to detect hidden dependencies, like &amp;lt;code&amp;gt;pkg-config&amp;lt;/code&amp;gt; silently invoked by autotools.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Once time setup.&#039;&#039;&#039; Poudriere is installed in devserver role. To use it you need:&lt;br /&gt;
  - a FreeBSD ports tree, let&#039;s say you clone it to /home/luser/dev/freebsd/ports&lt;br /&gt;
  - to declare `luser-dev: /home/luser/dev/freebsd/ports` to {{Ops file|pillar/devserver/ports.sls}} under poudriere.ports&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Build a port.&#039;&#039;&#039; Then you can work in /home/luser/dev/freebsd/ports and build through:&lt;br /&gt;
&lt;br /&gt;
    sudo poudriere testport -j base14 -p luser_dev -o somecategory/someport&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Logs.&#039;&#039;&#039; You can browse logs on the web, as https://windriver.nasqueron.org/poudriere serves &amp;lt;code&amp;gt;/usr/local/poudriere/data/logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache.&#039;&#039;&#039; To speed up recompilation, [https://ccache.dev/ ccache] is used as disk cache. Metrics for the cache are available for WindRiver at http://172.27.27.35:2337/metrics/ccache (curl it from the server).&lt;br /&gt;
&lt;br /&gt;
== Databases ==&lt;br /&gt;
&lt;br /&gt;
=== MariaDB ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-mysql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
You&#039;ve access to test_% databases, so you can without any specific configuration use that for unit tests. It&#039;s even OK to connect without login or password for the test_% namespace.&lt;br /&gt;
&lt;br /&gt;
If you need any other database and app config, you can request it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20MariaDB%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
For long term projects, you can also specify where you want the credentials to be be provisioned,&lt;br /&gt;
or request a Vault AppRole for your application to fetch them securely.&lt;br /&gt;
&lt;br /&gt;
=== PostgreSQL ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-pgsql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20PostgreSQL%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Redis ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;For licensing purpose, should be replaced by Valkey or upgraded to a recent version under AGPLv3. See [https://devcentral.nasqueron.org/T1938 this discussion on DevCentral] for more context.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Redis is available on WindRiver, with database 0 used by NetBox.&lt;br /&gt;
&lt;br /&gt;
== General build information ==&lt;br /&gt;
=== Number of CPUS ===&lt;br /&gt;
On FreeBSD, the number of CPU core can be given by &amp;lt;code&amp;gt;sysctl hw.ncpu&amp;lt;/code&amp;gt; while the full XML description is available at &amp;lt;code&amp;gt;sysctl kern.sched.topology_spec&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Modern CPU allow for parallel compile tasks, use that.&lt;br /&gt;
&lt;br /&gt;
For example, as of October 2024, you can use 12 core to build on WindRiver.&lt;br /&gt;
&lt;br /&gt;
=== ccache ===&lt;br /&gt;
A compiler cache is installed with ccache.&lt;br /&gt;
&lt;br /&gt;
The /etc/make.conf configuration file defines /var/cache/ccache directory to be used for ports build.&lt;br /&gt;
That directory can&#039;t only be written as root.&lt;br /&gt;
&lt;br /&gt;
You can create your own configuration in &amp;lt;code&amp;gt;$HOME/.ccache/ccache.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
The exact path and configuration values can be get by &amp;lt;code&amp;gt;ccache -s&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Rust ==&lt;br /&gt;
=== Rustup ===&lt;br /&gt;
If you wish to have control of what stable and nightly Rust versions to use, you can use Rustup.&lt;br /&gt;
&lt;br /&gt;
To use it, the command &amp;lt;code&amp;gt;rustup-init&amp;lt;/code&amp;gt; is available as an alternative to the curl magic documented on the Rustup web site (actually it&#039;s a fairly recent version of that script saved locally).&lt;br /&gt;
&lt;br /&gt;
=== cargo, rustc ===&lt;br /&gt;
If you&#039;re OK with the Rust version installed by OS packages, feel free to call directly cargo or rustc.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
You can generate project documentation with the `cargo doc` command.&lt;br /&gt;
&lt;br /&gt;
== PHP ==&lt;br /&gt;
=== Tools provided ===&lt;br /&gt;
The following software are maintained as phar or repository clones in /opt:&lt;br /&gt;
* psysh, REPL shell to test PHP code (with PHP documentation available at /usr/local/share/psysh/php_manual.sqlite)&lt;br /&gt;
* psalm, for static analysis&lt;br /&gt;
* doctum and phpdoc (phpDocumentor) to generate documentation&lt;br /&gt;
* phpunit&lt;br /&gt;
* phpcs&lt;br /&gt;
&lt;br /&gt;
Most of those tools have wrappers in /usr/local/bin trying to detect if a version is installed locally for the PHP project through Composer and switching to that version if available.&lt;br /&gt;
&lt;br /&gt;
So by running &amp;quot;phpunit&amp;quot;, you&#039;ll run your Composer version and the system version when missing.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
&lt;br /&gt;
To generate documentation for your code, &amp;lt;code&amp;gt;phpdoc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;doctum&amp;lt;/code&amp;gt; commands are available, including GraphViz and PlantUML for phpDocumentor classes graphes.&lt;br /&gt;
&lt;br /&gt;
== Network use ==&lt;br /&gt;
=== Port to use to spawn a server ===&lt;br /&gt;
To spawn a temporary web server, you can use ports 1080, 8000 or 8080 for a recommended 30 minutes maximum duration.&lt;br /&gt;
&lt;br /&gt;
For longer temporary need, the ports 3000, 3100, 3300, 3400, 3500, 3600, 3700, 3800 and 3900 are recommended.&lt;br /&gt;
&lt;br /&gt;
Other applications server can use any free port. Any long-term server should add an entry into {{Ops file|PORTS}}.&lt;br /&gt;
&lt;br /&gt;
== SSH tunnels ==&lt;br /&gt;
You can connect to the devserver and create one or more tunnels where you need:&lt;br /&gt;
* directly on the devserver: &amp;lt;code&amp;gt;ssh -L 3306:localhost:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
* to another server: &amp;lt;code&amp;gt;ssh -L 3307:db-B-001:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Format is always -L &amp;lt;port on your computer&amp;gt;:&amp;lt;server ip or hostname&amp;gt;:&amp;lt;port on the server&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resources are reachable on your computer going to localhost and the port you specified locally (e.g. localhost:3307 for db-B-001 MariaDB).&lt;br /&gt;
&lt;br /&gt;
=== Certificates for private network resources ===&lt;br /&gt;
Resources using TLS connections on the private network use certificates managed by the CA we&#039;ve set up with Vault.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to install on your computer the root certificate: {{Ops file|roles/core/certificates/files/nasqueron-vault-ca.crt}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - Linux and UNIX&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Step 1. Install package !! Step 2. Drop .crt in directory !! Step 3. Run update command&lt;br /&gt;
|-&lt;br /&gt;
| Arch Linux || ca-certificates-utils || /etc/ca-certificates/trust-source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| Debian/Ubuntu|| ca-certificates || /usr/local/share/ca-certificates || update-ca-certificates&lt;br /&gt;
|-&lt;br /&gt;
| Fedora/RHEL || ca-certificates || /etc/pki/ca-trust/source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| FreeBSD || ca_root_nss || /usr/local/share/certs || &#039;&#039;You&#039;re done, nothing to run :)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| openSUSE || ca-certificates-mozilla || /etc/pki/trust/anchors || update-ca-certificates&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - OS using GUI&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Procedure&lt;br /&gt;
|-&lt;br /&gt;
| ChromeOS || Chrome browser → Settings → Privacy and security → Security → Manage certificates → Authorities → Import&lt;br /&gt;
|-&lt;br /&gt;
| macOS || Keychain Access → System keychain → File &amp;gt; Import Items…&lt;br /&gt;
|-&lt;br /&gt;
| Windows || Import into “Trusted Root Certification Authorities” (mmc → Certificates snap-in)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Acceptable use policy ==&lt;br /&gt;
&amp;lt;poem&amp;gt;&lt;br /&gt;
&#039;&#039;The devserver is a shared space built on trust.&#039;&#039;&lt;br /&gt;
&#039;&#039;It exists to enable creativity, learning, and collaboration, not to enforce restrictive usage rules.&#039;&#039;&lt;br /&gt;
&amp;lt;/poem&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The devserver is primarily intended to:&lt;br /&gt;
&lt;br /&gt;
* support any activity related to Nasqueron;&lt;br /&gt;
* support contributions to open source software and free culture in general;&lt;br /&gt;
* support participation in technical workshops organized with Nasqueron.&lt;br /&gt;
&lt;br /&gt;
Any activity falling within these purposes is encouraged.&lt;br /&gt;
&lt;br /&gt;
The devserver may also be used to:&lt;br /&gt;
&lt;br /&gt;
* host personal content, provided resource usage remains reasonable (disk space, CPU, RAM, bandwidth);&lt;br /&gt;
* support activities aimed at learning, experimentation, and knowledge sharing.&lt;br /&gt;
&lt;br /&gt;
There is no requirement for all hosted content to be open source. Closed-source software, personal documents, or non-public projects are acceptable as long as they do not interfere with the normal operation of the service or other users.&lt;br /&gt;
&lt;br /&gt;
IRC usage should normally take place on Eglide.&lt;br /&gt;
However, hosting a lightweight IRC client (such as irssi or weechat) on another devserver, for example to avoid managing multiple SSH or tmux sessions, is acceptable.&lt;br /&gt;
&lt;br /&gt;
== Personal notes ==&lt;br /&gt;
&lt;br /&gt;
You can add in this section links to your own personal notes to use the devserver for your activities, including ideally a disaster recovery plan (how do you reinstall the code your work with?)&lt;br /&gt;
&lt;br /&gt;
* [[User:Dereckson/Devserver]]&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== You&#039;ve lost a file ===&lt;br /&gt;
&lt;br /&gt;
Your home directory has snapshots every 5 minutes, you can get back your recently lost file from a read-only snapshot:&lt;br /&gt;
&lt;br /&gt;
    cd ~/.zfs/snapshot&lt;br /&gt;
    ls&lt;br /&gt;
&lt;br /&gt;
Those are folders with your home directory content as it was at the snapshot time, you can even diff files to spot differences:&lt;br /&gt;
&lt;br /&gt;
    cd zfs-auto-snap_hourly-2023-06-05-20h00&lt;br /&gt;
    diff -u dev/nasqueron/operations/alkane/Jenkinsfile ~/dev/nasqueron/operations/alkane/Jenkinsfile&lt;br /&gt;
&lt;br /&gt;
You&#039;ve 12 months to act if your file is already in a monthly snapshot, but only 2 hours if it&#039;s in the last frequent snapshot and you keep modifying files, so act as quick as possible, ie right now. If you can&#039;t act right now, at least save the snapshot having the file (check that to avoid to save the wrong one) you want to recover with &amp;lt;code&amp;gt;zfs hold&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    zfs hold keep arcology/usr/home/dereckson@zfs-auto-snap_hourly-2023-06-05-22h00&lt;br /&gt;
&lt;br /&gt;
=== SSH issues ===&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve a message like &amp;lt;code&amp;gt;username@windriver.nasqueron.org: Permission denied (publickey).&amp;lt;/code&amp;gt;, it can means:&lt;br /&gt;
&lt;br /&gt;
* No SSH key has been offered&lt;br /&gt;
* SSH keys have been offered, but not recognized by the server&lt;br /&gt;
** Check {{Ops file|pillar/core/users.sls}} - do you have your public key on it?&lt;br /&gt;
** If not, and you have a new key, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add your new SSH key]&lt;br /&gt;
* SSH key is in a non-standard path - launch a SSH agent if needed (you&#039;ve already one with your desktop engine), then ssh-add &amp;lt;path to key&amp;gt;&lt;br /&gt;
* SSH key is protected by a passphrase and you don&#039;t remember the password&lt;br /&gt;
** Solution 1. Try to remember the password, with an agent and ssh-add, it will be less confusing as you&#039;ll work locally&lt;br /&gt;
** Solution 2. Destroy the SSH key, create a new one, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add that new key], login or ssh-add each day for one week to remember it&lt;br /&gt;
&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:PHP]]&lt;br /&gt;
[[Category:ZFS]]&lt;br /&gt;
[[Category:Contributor guide]]&lt;br /&gt;
[[Category:Reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2605</id>
		<title>Devserver reference</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2605"/>
		<updated>2026-09-13T11:19:35Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Userland */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nasqueron Operations SIG maintains &#039;&#039;&#039;devservers&#039;&#039;&#039;. They offer a remote development environment for Nasqueron and open source projects.&lt;br /&gt;
&lt;br /&gt;
== General information ==&lt;br /&gt;
=== What server to use? ===&lt;br /&gt;
&lt;br /&gt;
* WindRiver, for any general-purpose task&lt;br /&gt;
* Ysul, for webserver-legacy sites&lt;br /&gt;
* Dwellers, for Docker development&lt;br /&gt;
* Eglide, if you need an IRC session&lt;br /&gt;
&lt;br /&gt;
=== How to get access? ===&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/3/&lt;br /&gt;
  | text = Request account&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
You can ask it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
==== Do it yourself ====&lt;br /&gt;
&lt;br /&gt;
You&#039;re encouraged to submit a change with the configuration for your own access. That helps to speed up the process and ensure the right information is there:&lt;br /&gt;
&lt;br /&gt;
* Familiar with Git and our contributions workflow? follow NOG instructions at [[Operations grimoire/Create and revoke user accounts on Salt servers]] and send a commit to DevCentral against our operations repository.&lt;br /&gt;
* Newcomer ? We also have a full guide in French explaining step by step how to create that change on a Windows system and send it to review: [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Groups to use&lt;br /&gt;
|-&lt;br /&gt;
! Server !! Group name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Ysul / WindRiver || nasquenautes || for general purpose development&lt;br /&gt;
|-&lt;br /&gt;
| Dwellers || nasqueron-dev-docker || for Docker development&lt;br /&gt;
|-&lt;br /&gt;
| Eglide || shell || for IRC purpose&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Userland ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Normally, devservers run under FreeBSD. That means your userland is a BSD one, and some commands arguments can be different if you&#039;re used to Linux. GNU coretools utilities are installed and prefixed by the letter g, e.g. gmake or gsed.&lt;br /&gt;
&lt;br /&gt;
The most useful GNU commands are:&lt;br /&gt;
* gmake when the Makefile has been written for GNU make&lt;br /&gt;
* gsed -i to perform inline replacements&lt;br /&gt;
* gwatch (/usr/sbin/watch allows the user to examine all data coming through a specified tty, that&#039;s quite different)&lt;br /&gt;
&lt;br /&gt;
Note grep is the BSD one, but documented to be GNU compatible.&lt;br /&gt;
&lt;br /&gt;
=== Directories ===&lt;br /&gt;
* Use your /home/luser directory for anything personal&lt;br /&gt;
* Use /srv or /var when you need to share something, that generally requires a Salt config to give shared rights to that directory&lt;br /&gt;
* /var/51-wwwroot on Ysul is to use instead of your home folder for staging area of web applications you want to serve&lt;br /&gt;
* /datacube contains separate ZFS filesystems for a chunk of data, for example a backup, a collection of Git repositories, they can be declared in {{Ops file|pillar/devserver/datacubes.sls}}&lt;br /&gt;
&lt;br /&gt;
=== tmux ===&lt;br /&gt;
* You can use &amp;lt;code&amp;gt;reptyr &amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt; to attach a process spawned outside a tmux session in one&lt;br /&gt;
* [https://gist.github.com/MohamedAlaa/2961058 tmux cheat sheet]&lt;br /&gt;
&lt;br /&gt;
=== Mouse support ===&lt;br /&gt;
* nano allows to enable mouse mode with meta+m (ESC, m or alt + m)&lt;br /&gt;
** you can click on any character you want to navigate to&lt;br /&gt;
** if you initiate a selection with shift + up/down, you can extend it with the mouse&lt;br /&gt;
* in tmux, mouse mode (to configure, for example ctrl + b, m) allows you to resize panes&lt;br /&gt;
* mouse scroll (mouse wheel or swipe right of your touchpad) generally works like page up / page down to scroll in a buffer&lt;br /&gt;
&lt;br /&gt;
=== Modern Neovim support ===&lt;br /&gt;
&lt;br /&gt;
You can run &amp;lt;code&amp;gt;vi&amp;lt;/code&amp;gt; (very old nvi, shipped with FreeBSD OS), &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; for traditional Vim or &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; for modern Neovim environment.&lt;br /&gt;
&lt;br /&gt;
We recommend to use &amp;lt;code&amp;gt;nvim&amp;lt;/code&amp;gt; and support the [https://www.astronvim.com/ AstroNvim] distribution. All system dependencies are already installed and managed via Salt.&lt;br /&gt;
&lt;br /&gt;
To start with AstroNvim:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/.config&lt;br /&gt;
git clone https://github.com/AstroNvim/template ~/.config/nvim&lt;br /&gt;
nvim&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the first launch, Neovim will automatically download all required plugins and compile Treesitter parsers. This may take a minute. Once finished, you can type &amp;lt;code&amp;gt;:qa&amp;lt;/code&amp;gt; to exit and relaunch.&lt;br /&gt;
&lt;br /&gt;
It&#039;s now ready to use. To add support for a specific language, type &amp;lt;code&amp;gt;:Mason&amp;lt;/code&amp;gt; and search for the language server relevant to your development.&lt;br /&gt;
&lt;br /&gt;
LazyNvim and other configuration frameworks built upon lazy.nvim should work too.&lt;br /&gt;
&lt;br /&gt;
The devservers provide a fully provisioned environment for modern Neovim development. All system dependencies required by  (including &amp;lt;code&amp;gt;neovim&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ripgrep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;node&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;npm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tree-sitter-cli&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;gmake&amp;lt;/code&amp;gt; for FreeBSD) &lt;br /&gt;
&lt;br /&gt;
=== Customize themes ===&lt;br /&gt;
==== Save configuration files ====&lt;br /&gt;
&lt;br /&gt;
You can commit directly in the Operations repository your own dotfiles to configure your applications themes, or any other settings.&lt;br /&gt;
&lt;br /&gt;
Create a directory with your username at the following location:&lt;br /&gt;
* for every server: {{Ops file|roles/core/userland-home/files}}&lt;br /&gt;
* for devserver only: {{Ops file|roles/devserver/userland-home/files}}&lt;br /&gt;
&lt;br /&gt;
Content from those directories will be directly deployed in your home directory.&lt;br /&gt;
&lt;br /&gt;
==== Nerd Font and Powerline ====&lt;br /&gt;
&lt;br /&gt;
SSH server accepts the POWERLINE_SUPPORTED environment variable, so you can add in your own client shell configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
export POWERLINE_SUPPORTED=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then on the server, you can read it to apply a different theme for your prompt, tmux, etc. when you&#039;ve this variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
if [[ -n &amp;quot;$POWERLINE_SUPPORTED&amp;quot; ]]; then&lt;br /&gt;
    source ~/.config/zsh/powerline-prompt.zsh&lt;br /&gt;
else&lt;br /&gt;
    source ~/.config/zsh/fallback-prompt.zsh&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We&#039;re open to other ideas to improve better integration with client themes.&lt;br /&gt;
If you save any setting in Operations repository, you can attach {{T|2361}} as a reference.&lt;br /&gt;
&lt;br /&gt;
== FreeBSD development ==&lt;br /&gt;
&lt;br /&gt;
For FreeBSD development tasks, WindRiver could be suitable if you need a amd64 arch.&lt;br /&gt;
It&#039;s certainly suitable for ports development, as Poudriere is installed there.&lt;br /&gt;
&lt;br /&gt;
=== Ports ===&lt;br /&gt;
==== Poudriere ====&lt;br /&gt;
The [https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere FreeBSD porter handbook suggests Poudriere] as a convenient way to test ports in a clean environment.&lt;br /&gt;
&lt;br /&gt;
One of the big interest of this method is to start from scratch, allowing to detect hidden dependencies, like &amp;lt;code&amp;gt;pkg-config&amp;lt;/code&amp;gt; silently invoked by autotools.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Once time setup.&#039;&#039;&#039; Poudriere is installed in devserver role. To use it you need:&lt;br /&gt;
  - a FreeBSD ports tree, let&#039;s say you clone it to /home/luser/dev/freebsd/ports&lt;br /&gt;
  - to declare `luser-dev: /home/luser/dev/freebsd/ports` to {{Ops file|pillar/devserver/ports.sls}} under poudriere.ports&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Build a port.&#039;&#039;&#039; Then you can work in /home/luser/dev/freebsd/ports and build through:&lt;br /&gt;
&lt;br /&gt;
    sudo poudriere testport -j base14 -p luser_dev -o somecategory/someport&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Logs.&#039;&#039;&#039; You can browse logs on the web, as https://windriver.nasqueron.org/poudriere serves &amp;lt;code&amp;gt;/usr/local/poudriere/data/logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache.&#039;&#039;&#039; To speed up recompilation, [https://ccache.dev/ ccache] is used as disk cache. Metrics for the cache are available for WindRiver at http://172.27.27.35:2337/metrics/ccache (curl it from the server).&lt;br /&gt;
&lt;br /&gt;
== Databases ==&lt;br /&gt;
&lt;br /&gt;
=== MariaDB ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-mysql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
You&#039;ve access to test_% databases, so you can without any specific configuration use that for unit tests. It&#039;s even OK to connect without login or password for the test_% namespace.&lt;br /&gt;
&lt;br /&gt;
If you need any other database and app config, you can request it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20MariaDB%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
For long term projects, you can also specify where you want the credentials to be be provisioned,&lt;br /&gt;
or request a Vault AppRole for your application to fetch them securely.&lt;br /&gt;
&lt;br /&gt;
=== PostgreSQL ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-pgsql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20PostgreSQL%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Redis ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;For licensing purpose, should be replaced by Valkey or upgraded to a recent version under AGPLv3. See [https://devcentral.nasqueron.org/T1938 this discussion on DevCentral] for more context.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Redis is available on WindRiver, with database 0 used by NetBox.&lt;br /&gt;
&lt;br /&gt;
== General build information ==&lt;br /&gt;
=== Number of CPUS ===&lt;br /&gt;
On FreeBSD, the number of CPU core can be given by &amp;lt;code&amp;gt;sysctl hw.ncpu&amp;lt;/code&amp;gt; while the full XML description is available at &amp;lt;code&amp;gt;sysctl kern.sched.topology_spec&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Modern CPU allow for parallel compile tasks, use that.&lt;br /&gt;
&lt;br /&gt;
For example, as of October 2024, you can use 12 core to build on WindRiver.&lt;br /&gt;
&lt;br /&gt;
=== ccache ===&lt;br /&gt;
A compiler cache is installed with ccache.&lt;br /&gt;
&lt;br /&gt;
The /etc/make.conf configuration file defines /var/cache/ccache directory to be used for ports build.&lt;br /&gt;
That directory can&#039;t only be written as root.&lt;br /&gt;
&lt;br /&gt;
You can create your own configuration in &amp;lt;code&amp;gt;$HOME/.ccache/ccache.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
The exact path and configuration values can be get by &amp;lt;code&amp;gt;ccache -s&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Rust ==&lt;br /&gt;
=== Rustup ===&lt;br /&gt;
If you wish to have control of what stable and nightly Rust versions to use, you can use Rustup.&lt;br /&gt;
&lt;br /&gt;
To use it, the command &amp;lt;code&amp;gt;rustup-init&amp;lt;/code&amp;gt; is available as an alternative to the curl magic documented on the Rustup web site (actually it&#039;s a fairly recent version of that script saved locally).&lt;br /&gt;
&lt;br /&gt;
=== cargo, rustc ===&lt;br /&gt;
If you&#039;re OK with the Rust version installed by OS packages, feel free to call directly cargo or rustc.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
You can generate project documentation with the `cargo doc` command.&lt;br /&gt;
&lt;br /&gt;
== PHP ==&lt;br /&gt;
=== Tools provided ===&lt;br /&gt;
The following software are maintained as phar or repository clones in /opt:&lt;br /&gt;
* psysh, REPL shell to test PHP code (with PHP documentation available at /usr/local/share/psysh/php_manual.sqlite)&lt;br /&gt;
* psalm, for static analysis&lt;br /&gt;
* doctum and phpdoc (phpDocumentor) to generate documentation&lt;br /&gt;
* phpunit&lt;br /&gt;
* phpcs&lt;br /&gt;
&lt;br /&gt;
Most of those tools have wrappers in /usr/local/bin trying to detect if a version is installed locally for the PHP project through Composer and switching to that version if available.&lt;br /&gt;
&lt;br /&gt;
So by running &amp;quot;phpunit&amp;quot;, you&#039;ll run your Composer version and the system version when missing.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
&lt;br /&gt;
To generate documentation for your code, &amp;lt;code&amp;gt;phpdoc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;doctum&amp;lt;/code&amp;gt; commands are available, including GraphViz and PlantUML for phpDocumentor classes graphes.&lt;br /&gt;
&lt;br /&gt;
== Network use ==&lt;br /&gt;
=== Port to use to spawn a server ===&lt;br /&gt;
To spawn a temporary web server, you can use ports 1080, 8000 or 8080 for a recommended 30 minutes maximum duration.&lt;br /&gt;
&lt;br /&gt;
For longer temporary need, the ports 3000, 3100, 3300, 3400, 3500, 3600, 3700, 3800 and 3900 are recommended.&lt;br /&gt;
&lt;br /&gt;
Other applications server can use any free port. Any long-term server should add an entry into {{Ops file|PORTS}}.&lt;br /&gt;
&lt;br /&gt;
== SSH tunnels ==&lt;br /&gt;
You can connect to the devserver and create one or more tunnels where you need:&lt;br /&gt;
* directly on the devserver: &amp;lt;code&amp;gt;ssh -L 3306:localhost:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
* to another server: &amp;lt;code&amp;gt;ssh -L 3307:db-B-001:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Format is always -L &amp;lt;port on your computer&amp;gt;:&amp;lt;server ip or hostname&amp;gt;:&amp;lt;port on the server&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resources are reachable on your computer going to localhost and the port you specified locally (e.g. localhost:3307 for db-B-001 MariaDB).&lt;br /&gt;
&lt;br /&gt;
=== Certificates for private network resources ===&lt;br /&gt;
Resources using TLS connections on the private network use certificates managed by the CA we&#039;ve set up with Vault.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to install on your computer the root certificate: {{Ops file|roles/core/certificates/files/nasqueron-vault-ca.crt}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - Linux and UNIX&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Step 1. Install package !! Step 2. Drop .crt in directory !! Step 3. Run update command&lt;br /&gt;
|-&lt;br /&gt;
| Arch Linux || ca-certificates-utils || /etc/ca-certificates/trust-source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| Debian/Ubuntu|| ca-certificates || /usr/local/share/ca-certificates || update-ca-certificates&lt;br /&gt;
|-&lt;br /&gt;
| Fedora/RHEL || ca-certificates || /etc/pki/ca-trust/source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| FreeBSD || ca_root_nss || /usr/local/share/certs || &#039;&#039;You&#039;re done, nothing to run :)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| openSUSE || ca-certificates-mozilla || /etc/pki/trust/anchors || update-ca-certificates&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - OS using GUI&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Procedure&lt;br /&gt;
|-&lt;br /&gt;
| ChromeOS || Chrome browser → Settings → Privacy and security → Security → Manage certificates → Authorities → Import&lt;br /&gt;
|-&lt;br /&gt;
| macOS || Keychain Access → System keychain → File &amp;gt; Import Items…&lt;br /&gt;
|-&lt;br /&gt;
| Windows || Import into “Trusted Root Certification Authorities” (mmc → Certificates snap-in)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Acceptable use policy ==&lt;br /&gt;
&amp;lt;poem&amp;gt;&lt;br /&gt;
&#039;&#039;The devserver is a shared space built on trust.&#039;&#039;&lt;br /&gt;
&#039;&#039;It exists to enable creativity, learning, and collaboration, not to enforce restrictive usage rules.&#039;&#039;&lt;br /&gt;
&amp;lt;/poem&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The devserver is primarily intended to:&lt;br /&gt;
&lt;br /&gt;
* support any activity related to Nasqueron;&lt;br /&gt;
* support contributions to open source software and free culture in general;&lt;br /&gt;
* support participation in technical workshops organized with Nasqueron.&lt;br /&gt;
&lt;br /&gt;
Any activity falling within these purposes is encouraged.&lt;br /&gt;
&lt;br /&gt;
The devserver may also be used to:&lt;br /&gt;
&lt;br /&gt;
* host personal content, provided resource usage remains reasonable (disk space, CPU, RAM, bandwidth);&lt;br /&gt;
* support activities aimed at learning, experimentation, and knowledge sharing.&lt;br /&gt;
&lt;br /&gt;
There is no requirement for all hosted content to be open source. Closed-source software, personal documents, or non-public projects are acceptable as long as they do not interfere with the normal operation of the service or other users.&lt;br /&gt;
&lt;br /&gt;
IRC usage should normally take place on Eglide.&lt;br /&gt;
However, hosting a lightweight IRC client (such as irssi or weechat) on another devserver, for example to avoid managing multiple SSH or tmux sessions, is acceptable.&lt;br /&gt;
&lt;br /&gt;
== Personal notes ==&lt;br /&gt;
&lt;br /&gt;
You can add in this section links to your own personal notes to use the devserver for your activities, including ideally a disaster recovery plan (how do you reinstall the code your work with?)&lt;br /&gt;
&lt;br /&gt;
* [[User:Dereckson/Devserver]]&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== You&#039;ve lost a file ===&lt;br /&gt;
&lt;br /&gt;
Your home directory has snapshots every 5 minutes, you can get back your recently lost file from a read-only snapshot:&lt;br /&gt;
&lt;br /&gt;
    cd ~/.zfs/snapshot&lt;br /&gt;
    ls&lt;br /&gt;
&lt;br /&gt;
Those are folders with your home directory content as it was at the snapshot time, you can even diff files to spot differences:&lt;br /&gt;
&lt;br /&gt;
    cd zfs-auto-snap_hourly-2023-06-05-20h00&lt;br /&gt;
    diff -u dev/nasqueron/operations/alkane/Jenkinsfile ~/dev/nasqueron/operations/alkane/Jenkinsfile&lt;br /&gt;
&lt;br /&gt;
You&#039;ve 12 months to act if your file is already in a monthly snapshot, but only 2 hours if it&#039;s in the last frequent snapshot and you keep modifying files, so act as quick as possible, ie right now. If you can&#039;t act right now, at least save the snapshot having the file (check that to avoid to save the wrong one) you want to recover with &amp;lt;code&amp;gt;zfs hold&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    zfs hold keep arcology/usr/home/dereckson@zfs-auto-snap_hourly-2023-06-05-22h00&lt;br /&gt;
&lt;br /&gt;
=== SSH issues ===&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve a message like &amp;lt;code&amp;gt;username@windriver.nasqueron.org: Permission denied (publickey).&amp;lt;/code&amp;gt;, it can means:&lt;br /&gt;
&lt;br /&gt;
* No SSH key has been offered&lt;br /&gt;
* SSH keys have been offered, but not recognized by the server&lt;br /&gt;
** Check {{Ops file|pillar/core/users.sls}} - do you have your public key on it?&lt;br /&gt;
** If not, and you have a new key, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add your new SSH key]&lt;br /&gt;
* SSH key is in a non-standard path - launch a SSH agent if needed (you&#039;ve already one with your desktop engine), then ssh-add &amp;lt;path to key&amp;gt;&lt;br /&gt;
* SSH key is protected by a passphrase and you don&#039;t remember the password&lt;br /&gt;
** Solution 1. Try to remember the password, with an agent and ssh-add, it will be less confusing as you&#039;ll work locally&lt;br /&gt;
** Solution 2. Destroy the SSH key, create a new one, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add that new key], login or ssh-add each day for one week to remember it&lt;br /&gt;
&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:PHP]]&lt;br /&gt;
[[Category:ZFS]]&lt;br /&gt;
[[Category:Contributor guide]]&lt;br /&gt;
[[Category:Reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=WindRiver&amp;diff=2604</id>
		<title>WindRiver</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=WindRiver&amp;diff=2604"/>
		<updated>2026-09-12T18:41:30Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Troubleshoot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;WindRiver is an FreeBSD 15 bare metal server for Nasqueron general-purpose development.&lt;br /&gt;
&lt;br /&gt;
It supersedes [[Ysul]].&lt;br /&gt;
&lt;br /&gt;
Upgrade is planned in September 2027, per [[Operations grimoire/Servers lifecycle]].&lt;br /&gt;
&lt;br /&gt;
Documentation: [[Devserver reference]]&lt;br /&gt;
&lt;br /&gt;
== Basic information ==&lt;br /&gt;
* &#039;&#039;&#039;IP:&#039;&#039;&#039;&lt;br /&gt;
** 195.154.30.15 &amp;lt;strike&amp;gt;51.159.17.168 since 2023-06-14. Previously 51.159.18.59.&amp;lt;/strike&amp;gt;&lt;br /&gt;
** 2a02:578:4f0d:0:30d0:7b22:6cde:a52e (native, still to migrate)&lt;br /&gt;
** 2001:bc8:6005:5:aa1e:84ff:fef3:5d9c (future through HE)&lt;br /&gt;
* &#039;&#039;&#039;Hostname:&#039;&#039;&#039; windriver.nasqueron.org&lt;br /&gt;
* &#039;&#039;&#039;Homepage:&#039;&#039;&#039; https://windriver.nasqueron.org/&lt;br /&gt;
* &#039;&#039;&#039;Configuration:&#039;&#039;&#039; &amp;lt;strike&amp;gt;64 Gb RAM, Xeon E3 1240 V6, 2x 500 GB SSD (&amp;quot;Pro-6-S&amp;quot;)&amp;lt;/strike&amp;gt; 96 Gb. RAM, Xeon E5-1650 v3, 3x 500 GB SSD (&amp;quot;Pro-4-L&amp;quot;)&lt;br /&gt;
* &#039;&#039;&#039;ISP:&#039;&#039;&#039; [http://www.online.net Online] (FR)&lt;br /&gt;
* &#039;&#039;&#039;Network:&#039;&#039;&#039; Illiad (FR)&lt;br /&gt;
* &#039;&#039;&#039;Policy:&#039;&#039;&#039; Access for any Nasqueron project and for infrastructure operations task.&lt;br /&gt;
* &#039;&#039;&#039;Started:&#039;&#039;&#039; 2019-11-21&lt;br /&gt;
&lt;br /&gt;
== Ports ==&lt;br /&gt;
&lt;br /&gt;
Ports for services managed automatically through Salt are available on {{Ops file|PORTS}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reserved ports - IRC server range&lt;br /&gt;
|-&lt;br /&gt;
! Port !! User account !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 6901 || dereckson || irssi proxy&lt;br /&gt;
|-&lt;br /&gt;
| 6902 || dereckson || irssi proxy&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
&lt;br /&gt;
=== Restore private network ===&lt;br /&gt;
&lt;br /&gt;
We made an error in 2026. We let students directly configure the current infrastructure instead of a sandbox environment to install a new router. At the end of their internship, they didn&#039;t merge their patches.&lt;br /&gt;
&lt;br /&gt;
As such, tunnels configured on WindRiver don&#039;t match the reality.&lt;br /&gt;
&lt;br /&gt;
As of 2026-09-12, to restore network:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ifconfig gre1 destroy&lt;br /&gt;
ifconfig gre0 destroy&lt;br /&gt;
ifconfig gre0 tunnel 195.154.30.15 178.32.70.110&lt;br /&gt;
ifconfig gre0 inet 172.27.27.35 172.27.27.243&lt;br /&gt;
route add 172.27.27.0/24 172.27.27.243&lt;br /&gt;
ifconfig igb0 inet6 2001:bc8:2e84:700:0:dead:c0de:b07 prefixlen 56 alias&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== To check after reboot ===&lt;br /&gt;
&lt;br /&gt;
* Network&lt;br /&gt;
** IPv4 - ICANN&lt;br /&gt;
** IPv6 - main + viperserv alias&lt;br /&gt;
** IPv6 - can we reach something with IPv6&lt;br /&gt;
** IPv4 - Drake: tunnel + route + ping any server&lt;br /&gt;
* Services&lt;br /&gt;
** Alkane HTTP server&lt;br /&gt;
** API exec HTTP server&lt;br /&gt;
** Bitlbee&lt;br /&gt;
** Eggdrops: Dæghrefn + Wearg&lt;br /&gt;
** Grafana&lt;br /&gt;
** MariaDB&lt;br /&gt;
** NetBox&lt;br /&gt;
** nginx&lt;br /&gt;
** Node exporter for Prometheus&lt;br /&gt;
** oidentd&lt;br /&gt;
** php-fpm&lt;br /&gt;
** PostgreSQL&lt;br /&gt;
** Prometheus&lt;br /&gt;
** Redis&lt;br /&gt;
** Salt minion&lt;br /&gt;
** SSH server&lt;br /&gt;
* Vault connectivity&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2603</id>
		<title>Devserver reference</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2603"/>
		<updated>2026-09-12T18:20:47Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Customize themes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nasqueron Operations SIG maintains &#039;&#039;&#039;devservers&#039;&#039;&#039;. They offer a remote development environment for Nasqueron and open source projects.&lt;br /&gt;
&lt;br /&gt;
== General information ==&lt;br /&gt;
=== What server to use? ===&lt;br /&gt;
&lt;br /&gt;
* WindRiver, for any general-purpose task&lt;br /&gt;
* Ysul, for webserver-legacy sites&lt;br /&gt;
* Dwellers, for Docker development&lt;br /&gt;
* Eglide, if you need an IRC session&lt;br /&gt;
&lt;br /&gt;
=== How to get access? ===&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/3/&lt;br /&gt;
  | text = Request account&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
You can ask it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
==== Do it yourself ====&lt;br /&gt;
&lt;br /&gt;
You&#039;re encouraged to submit a change with the configuration for your own access. That helps to speed up the process and ensure the right information is there:&lt;br /&gt;
&lt;br /&gt;
* Familiar with Git and our contributions workflow? follow NOG instructions at [[Operations grimoire/Create and revoke user accounts on Salt servers]] and send a commit to DevCentral against our operations repository.&lt;br /&gt;
* Newcomer ? We also have a full guide in French explaining step by step how to create that change on a Windows system and send it to review: [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Groups to use&lt;br /&gt;
|-&lt;br /&gt;
! Server !! Group name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Ysul / WindRiver || nasquenautes || for general purpose development&lt;br /&gt;
|-&lt;br /&gt;
| Dwellers || nasqueron-dev-docker || for Docker development&lt;br /&gt;
|-&lt;br /&gt;
| Eglide || shell || for IRC purpose&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Userland ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Normally, devservers run under FreeBSD. That means your userland is a BSD one, and some commands arguments can be different if you&#039;re used to Linux. GNU coretools utilities are installed and prefixed by the letter g, e.g. gmake or gsed.&lt;br /&gt;
&lt;br /&gt;
The most useful GNU commands are:&lt;br /&gt;
* gmake when the Makefile has been written for GNU make&lt;br /&gt;
* gsed -i to perform inline replacements&lt;br /&gt;
* gwatch (/usr/sbin/watch allows the user to examine all data coming through a specified tty, that&#039;s quite different)&lt;br /&gt;
&lt;br /&gt;
Note grep is the BSD one, but documented to be GNU compatible.&lt;br /&gt;
&lt;br /&gt;
=== Directories ===&lt;br /&gt;
* Use your /home/luser directory for anything personal&lt;br /&gt;
* Use /srv or /var when you need to share something, that generally requires a Salt config to give shared rights to that directory&lt;br /&gt;
* /var/51-wwwroot on Ysul is to use instead of your home folder for staging area of web applications you want to serve&lt;br /&gt;
* /datacube contains separate ZFS filesystems for a chunk of data, for example a backup, a collection of Git repositories, they can be declared in {{Ops file|pillar/devserver/datacubes.sls}}&lt;br /&gt;
&lt;br /&gt;
=== tmux ===&lt;br /&gt;
* You can use &amp;lt;code&amp;gt;reptyr &amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt; to attach a process spawned outside a tmux session in one&lt;br /&gt;
* [https://gist.github.com/MohamedAlaa/2961058 tmux cheat sheet]&lt;br /&gt;
&lt;br /&gt;
=== Mouse support ===&lt;br /&gt;
* nano allows to enable mouse mode with meta+m (ESC, m or alt + m)&lt;br /&gt;
** you can click on any character you want to navigate to&lt;br /&gt;
** if you initiate a selection with shift + up/down, you can extend it with the mouse&lt;br /&gt;
* in tmux, mouse mode (to configure, for example ctrl + b, m) allows you to resize panes&lt;br /&gt;
* mouse scroll (mouse wheel or swipe right of your touchpad) generally works like page up / page down to scroll in a buffer&lt;br /&gt;
&lt;br /&gt;
=== Customize themes ===&lt;br /&gt;
==== Save configuration files ====&lt;br /&gt;
&lt;br /&gt;
You can commit directly in the Operations repository your own dotfiles to configure your applications themes, or any other settings.&lt;br /&gt;
&lt;br /&gt;
Create a directory with your username at the following location:&lt;br /&gt;
* for every server: {{Ops file|roles/core/userland-home/files}}&lt;br /&gt;
* for devserver only: {{Ops file|roles/devserver/userland-home/files}}&lt;br /&gt;
&lt;br /&gt;
Content from those directories will be directly deployed in your home directory.&lt;br /&gt;
&lt;br /&gt;
==== Nerd Font and Powerline ====&lt;br /&gt;
&lt;br /&gt;
SSH server accepts the POWERLINE_SUPPORTED environment variable, so you can add in your own client shell configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
export POWERLINE_SUPPORTED=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then on the server, you can read it to apply a different theme for your prompt, tmux, etc. when you&#039;ve this variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
if [[ -n &amp;quot;$POWERLINE_SUPPORTED&amp;quot; ]]; then&lt;br /&gt;
    source ~/.config/zsh/powerline-prompt.zsh&lt;br /&gt;
else&lt;br /&gt;
    source ~/.config/zsh/fallback-prompt.zsh&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We&#039;re open to other ideas to improve better integration with client themes.&lt;br /&gt;
If you save any setting in Operations repository, you can attach {{T|2361}} as a reference.&lt;br /&gt;
&lt;br /&gt;
== FreeBSD development ==&lt;br /&gt;
&lt;br /&gt;
For FreeBSD development tasks, WindRiver could be suitable if you need a amd64 arch.&lt;br /&gt;
It&#039;s certainly suitable for ports development, as Poudriere is installed there.&lt;br /&gt;
&lt;br /&gt;
=== Ports ===&lt;br /&gt;
==== Poudriere ====&lt;br /&gt;
The [https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere FreeBSD porter handbook suggests Poudriere] as a convenient way to test ports in a clean environment.&lt;br /&gt;
&lt;br /&gt;
One of the big interest of this method is to start from scratch, allowing to detect hidden dependencies, like &amp;lt;code&amp;gt;pkg-config&amp;lt;/code&amp;gt; silently invoked by autotools.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Once time setup.&#039;&#039;&#039; Poudriere is installed in devserver role. To use it you need:&lt;br /&gt;
  - a FreeBSD ports tree, let&#039;s say you clone it to /home/luser/dev/freebsd/ports&lt;br /&gt;
  - to declare `luser-dev: /home/luser/dev/freebsd/ports` to {{Ops file|pillar/devserver/ports.sls}} under poudriere.ports&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Build a port.&#039;&#039;&#039; Then you can work in /home/luser/dev/freebsd/ports and build through:&lt;br /&gt;
&lt;br /&gt;
    sudo poudriere testport -j base14 -p luser_dev -o somecategory/someport&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Logs.&#039;&#039;&#039; You can browse logs on the web, as https://windriver.nasqueron.org/poudriere serves &amp;lt;code&amp;gt;/usr/local/poudriere/data/logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache.&#039;&#039;&#039; To speed up recompilation, [https://ccache.dev/ ccache] is used as disk cache. Metrics for the cache are available for WindRiver at http://172.27.27.35:2337/metrics/ccache (curl it from the server).&lt;br /&gt;
&lt;br /&gt;
== Databases ==&lt;br /&gt;
&lt;br /&gt;
=== MariaDB ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-mysql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
You&#039;ve access to test_% databases, so you can without any specific configuration use that for unit tests. It&#039;s even OK to connect without login or password for the test_% namespace.&lt;br /&gt;
&lt;br /&gt;
If you need any other database and app config, you can request it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20MariaDB%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
For long term projects, you can also specify where you want the credentials to be be provisioned,&lt;br /&gt;
or request a Vault AppRole for your application to fetch them securely.&lt;br /&gt;
&lt;br /&gt;
=== PostgreSQL ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-pgsql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20PostgreSQL%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Redis ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;For licensing purpose, should be replaced by Valkey or upgraded to a recent version under AGPLv3. See [https://devcentral.nasqueron.org/T1938 this discussion on DevCentral] for more context.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Redis is available on WindRiver, with database 0 used by NetBox.&lt;br /&gt;
&lt;br /&gt;
== General build information ==&lt;br /&gt;
=== Number of CPUS ===&lt;br /&gt;
On FreeBSD, the number of CPU core can be given by &amp;lt;code&amp;gt;sysctl hw.ncpu&amp;lt;/code&amp;gt; while the full XML description is available at &amp;lt;code&amp;gt;sysctl kern.sched.topology_spec&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Modern CPU allow for parallel compile tasks, use that.&lt;br /&gt;
&lt;br /&gt;
For example, as of October 2024, you can use 12 core to build on WindRiver.&lt;br /&gt;
&lt;br /&gt;
=== ccache ===&lt;br /&gt;
A compiler cache is installed with ccache.&lt;br /&gt;
&lt;br /&gt;
The /etc/make.conf configuration file defines /var/cache/ccache directory to be used for ports build.&lt;br /&gt;
That directory can&#039;t only be written as root.&lt;br /&gt;
&lt;br /&gt;
You can create your own configuration in &amp;lt;code&amp;gt;$HOME/.ccache/ccache.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
The exact path and configuration values can be get by &amp;lt;code&amp;gt;ccache -s&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Rust ==&lt;br /&gt;
=== Rustup ===&lt;br /&gt;
If you wish to have control of what stable and nightly Rust versions to use, you can use Rustup.&lt;br /&gt;
&lt;br /&gt;
To use it, the command &amp;lt;code&amp;gt;rustup-init&amp;lt;/code&amp;gt; is available as an alternative to the curl magic documented on the Rustup web site (actually it&#039;s a fairly recent version of that script saved locally).&lt;br /&gt;
&lt;br /&gt;
=== cargo, rustc ===&lt;br /&gt;
If you&#039;re OK with the Rust version installed by OS packages, feel free to call directly cargo or rustc.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
You can generate project documentation with the `cargo doc` command.&lt;br /&gt;
&lt;br /&gt;
== PHP ==&lt;br /&gt;
=== Tools provided ===&lt;br /&gt;
The following software are maintained as phar or repository clones in /opt:&lt;br /&gt;
* psysh, REPL shell to test PHP code (with PHP documentation available at /usr/local/share/psysh/php_manual.sqlite)&lt;br /&gt;
* psalm, for static analysis&lt;br /&gt;
* doctum and phpdoc (phpDocumentor) to generate documentation&lt;br /&gt;
* phpunit&lt;br /&gt;
* phpcs&lt;br /&gt;
&lt;br /&gt;
Most of those tools have wrappers in /usr/local/bin trying to detect if a version is installed locally for the PHP project through Composer and switching to that version if available.&lt;br /&gt;
&lt;br /&gt;
So by running &amp;quot;phpunit&amp;quot;, you&#039;ll run your Composer version and the system version when missing.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
&lt;br /&gt;
To generate documentation for your code, &amp;lt;code&amp;gt;phpdoc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;doctum&amp;lt;/code&amp;gt; commands are available, including GraphViz and PlantUML for phpDocumentor classes graphes.&lt;br /&gt;
&lt;br /&gt;
== Network use ==&lt;br /&gt;
=== Port to use to spawn a server ===&lt;br /&gt;
To spawn a temporary web server, you can use ports 1080, 8000 or 8080 for a recommended 30 minutes maximum duration.&lt;br /&gt;
&lt;br /&gt;
For longer temporary need, the ports 3000, 3100, 3300, 3400, 3500, 3600, 3700, 3800 and 3900 are recommended.&lt;br /&gt;
&lt;br /&gt;
Other applications server can use any free port. Any long-term server should add an entry into {{Ops file|PORTS}}.&lt;br /&gt;
&lt;br /&gt;
== SSH tunnels ==&lt;br /&gt;
You can connect to the devserver and create one or more tunnels where you need:&lt;br /&gt;
* directly on the devserver: &amp;lt;code&amp;gt;ssh -L 3306:localhost:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
* to another server: &amp;lt;code&amp;gt;ssh -L 3307:db-B-001:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Format is always -L &amp;lt;port on your computer&amp;gt;:&amp;lt;server ip or hostname&amp;gt;:&amp;lt;port on the server&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resources are reachable on your computer going to localhost and the port you specified locally (e.g. localhost:3307 for db-B-001 MariaDB).&lt;br /&gt;
&lt;br /&gt;
=== Certificates for private network resources ===&lt;br /&gt;
Resources using TLS connections on the private network use certificates managed by the CA we&#039;ve set up with Vault.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to install on your computer the root certificate: {{Ops file|roles/core/certificates/files/nasqueron-vault-ca.crt}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - Linux and UNIX&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Step 1. Install package !! Step 2. Drop .crt in directory !! Step 3. Run update command&lt;br /&gt;
|-&lt;br /&gt;
| Arch Linux || ca-certificates-utils || /etc/ca-certificates/trust-source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| Debian/Ubuntu|| ca-certificates || /usr/local/share/ca-certificates || update-ca-certificates&lt;br /&gt;
|-&lt;br /&gt;
| Fedora/RHEL || ca-certificates || /etc/pki/ca-trust/source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| FreeBSD || ca_root_nss || /usr/local/share/certs || &#039;&#039;You&#039;re done, nothing to run :)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| openSUSE || ca-certificates-mozilla || /etc/pki/trust/anchors || update-ca-certificates&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - OS using GUI&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Procedure&lt;br /&gt;
|-&lt;br /&gt;
| ChromeOS || Chrome browser → Settings → Privacy and security → Security → Manage certificates → Authorities → Import&lt;br /&gt;
|-&lt;br /&gt;
| macOS || Keychain Access → System keychain → File &amp;gt; Import Items…&lt;br /&gt;
|-&lt;br /&gt;
| Windows || Import into “Trusted Root Certification Authorities” (mmc → Certificates snap-in)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Acceptable use policy ==&lt;br /&gt;
&amp;lt;poem&amp;gt;&lt;br /&gt;
&#039;&#039;The devserver is a shared space built on trust.&#039;&#039;&lt;br /&gt;
&#039;&#039;It exists to enable creativity, learning, and collaboration, not to enforce restrictive usage rules.&#039;&#039;&lt;br /&gt;
&amp;lt;/poem&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The devserver is primarily intended to:&lt;br /&gt;
&lt;br /&gt;
* support any activity related to Nasqueron;&lt;br /&gt;
* support contributions to open source software and free culture in general;&lt;br /&gt;
* support participation in technical workshops organized with Nasqueron.&lt;br /&gt;
&lt;br /&gt;
Any activity falling within these purposes is encouraged.&lt;br /&gt;
&lt;br /&gt;
The devserver may also be used to:&lt;br /&gt;
&lt;br /&gt;
* host personal content, provided resource usage remains reasonable (disk space, CPU, RAM, bandwidth);&lt;br /&gt;
* support activities aimed at learning, experimentation, and knowledge sharing.&lt;br /&gt;
&lt;br /&gt;
There is no requirement for all hosted content to be open source. Closed-source software, personal documents, or non-public projects are acceptable as long as they do not interfere with the normal operation of the service or other users.&lt;br /&gt;
&lt;br /&gt;
IRC usage should normally take place on Eglide.&lt;br /&gt;
However, hosting a lightweight IRC client (such as irssi or weechat) on another devserver, for example to avoid managing multiple SSH or tmux sessions, is acceptable.&lt;br /&gt;
&lt;br /&gt;
== Personal notes ==&lt;br /&gt;
&lt;br /&gt;
You can add in this section links to your own personal notes to use the devserver for your activities, including ideally a disaster recovery plan (how do you reinstall the code your work with?)&lt;br /&gt;
&lt;br /&gt;
* [[User:Dereckson/Devserver]]&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== You&#039;ve lost a file ===&lt;br /&gt;
&lt;br /&gt;
Your home directory has snapshots every 5 minutes, you can get back your recently lost file from a read-only snapshot:&lt;br /&gt;
&lt;br /&gt;
    cd ~/.zfs/snapshot&lt;br /&gt;
    ls&lt;br /&gt;
&lt;br /&gt;
Those are folders with your home directory content as it was at the snapshot time, you can even diff files to spot differences:&lt;br /&gt;
&lt;br /&gt;
    cd zfs-auto-snap_hourly-2023-06-05-20h00&lt;br /&gt;
    diff -u dev/nasqueron/operations/alkane/Jenkinsfile ~/dev/nasqueron/operations/alkane/Jenkinsfile&lt;br /&gt;
&lt;br /&gt;
You&#039;ve 12 months to act if your file is already in a monthly snapshot, but only 2 hours if it&#039;s in the last frequent snapshot and you keep modifying files, so act as quick as possible, ie right now. If you can&#039;t act right now, at least save the snapshot having the file (check that to avoid to save the wrong one) you want to recover with &amp;lt;code&amp;gt;zfs hold&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    zfs hold keep arcology/usr/home/dereckson@zfs-auto-snap_hourly-2023-06-05-22h00&lt;br /&gt;
&lt;br /&gt;
=== SSH issues ===&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve a message like &amp;lt;code&amp;gt;username@windriver.nasqueron.org: Permission denied (publickey).&amp;lt;/code&amp;gt;, it can means:&lt;br /&gt;
&lt;br /&gt;
* No SSH key has been offered&lt;br /&gt;
* SSH keys have been offered, but not recognized by the server&lt;br /&gt;
** Check {{Ops file|pillar/core/users.sls}} - do you have your public key on it?&lt;br /&gt;
** If not, and you have a new key, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add your new SSH key]&lt;br /&gt;
* SSH key is in a non-standard path - launch a SSH agent if needed (you&#039;ve already one with your desktop engine), then ssh-add &amp;lt;path to key&amp;gt;&lt;br /&gt;
* SSH key is protected by a passphrase and you don&#039;t remember the password&lt;br /&gt;
** Solution 1. Try to remember the password, with an agent and ssh-add, it will be less confusing as you&#039;ll work locally&lt;br /&gt;
** Solution 2. Destroy the SSH key, create a new one, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add that new key], login or ssh-add each day for one week to remember it&lt;br /&gt;
&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:PHP]]&lt;br /&gt;
[[Category:ZFS]]&lt;br /&gt;
[[Category:Contributor guide]]&lt;br /&gt;
[[Category:Reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2602</id>
		<title>Devserver reference</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Devserver_reference&amp;diff=2602"/>
		<updated>2026-09-12T18:16:33Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Userland */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nasqueron Operations SIG maintains &#039;&#039;&#039;devservers&#039;&#039;&#039;. They offer a remote development environment for Nasqueron and open source projects.&lt;br /&gt;
&lt;br /&gt;
== General information ==&lt;br /&gt;
=== What server to use? ===&lt;br /&gt;
&lt;br /&gt;
* WindRiver, for any general-purpose task&lt;br /&gt;
* Ysul, for webserver-legacy sites&lt;br /&gt;
* Dwellers, for Docker development&lt;br /&gt;
* Eglide, if you need an IRC session&lt;br /&gt;
&lt;br /&gt;
=== How to get access? ===&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/3/&lt;br /&gt;
  | text = Request account&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
You can ask it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
==== Do it yourself ====&lt;br /&gt;
&lt;br /&gt;
You&#039;re encouraged to submit a change with the configuration for your own access. That helps to speed up the process and ensure the right information is there:&lt;br /&gt;
&lt;br /&gt;
* Familiar with Git and our contributions workflow? follow NOG instructions at [[Operations grimoire/Create and revoke user accounts on Salt servers]] and send a commit to DevCentral against our operations repository.&lt;br /&gt;
* Newcomer ? We also have a full guide in French explaining step by step how to create that change on a Windows system and send it to review: [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Groups to use&lt;br /&gt;
|-&lt;br /&gt;
! Server !! Group name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Ysul / WindRiver || nasquenautes || for general purpose development&lt;br /&gt;
|-&lt;br /&gt;
| Dwellers || nasqueron-dev-docker || for Docker development&lt;br /&gt;
|-&lt;br /&gt;
| Eglide || shell || for IRC purpose&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Userland ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Normally, devservers run under FreeBSD. That means your userland is a BSD one, and some commands arguments can be different if you&#039;re used to Linux. GNU coretools utilities are installed and prefixed by the letter g, e.g. gmake or gsed.&lt;br /&gt;
&lt;br /&gt;
The most useful GNU commands are:&lt;br /&gt;
* gmake when the Makefile has been written for GNU make&lt;br /&gt;
* gsed -i to perform inline replacements&lt;br /&gt;
* gwatch (/usr/sbin/watch allows the user to examine all data coming through a specified tty, that&#039;s quite different)&lt;br /&gt;
&lt;br /&gt;
Note grep is the BSD one, but documented to be GNU compatible.&lt;br /&gt;
&lt;br /&gt;
=== Directories ===&lt;br /&gt;
* Use your /home/luser directory for anything personal&lt;br /&gt;
* Use /srv or /var when you need to share something, that generally requires a Salt config to give shared rights to that directory&lt;br /&gt;
* /var/51-wwwroot on Ysul is to use instead of your home folder for staging area of web applications you want to serve&lt;br /&gt;
* /datacube contains separate ZFS filesystems for a chunk of data, for example a backup, a collection of Git repositories, they can be declared in {{Ops file|pillar/devserver/datacubes.sls}}&lt;br /&gt;
&lt;br /&gt;
=== tmux ===&lt;br /&gt;
* You can use &amp;lt;code&amp;gt;reptyr &amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt; to attach a process spawned outside a tmux session in one&lt;br /&gt;
* [https://gist.github.com/MohamedAlaa/2961058 tmux cheat sheet]&lt;br /&gt;
&lt;br /&gt;
=== Mouse support ===&lt;br /&gt;
* nano allows to enable mouse mode with meta+m (ESC, m or alt + m)&lt;br /&gt;
** you can click on any character you want to navigate to&lt;br /&gt;
** if you initiate a selection with shift + up/down, you can extend it with the mouse&lt;br /&gt;
* in tmux, mouse mode (to configure, for example ctrl + b, m) allows you to resize panes&lt;br /&gt;
* mouse scroll (mouse wheel or swipe right of your touchpad) generally works like page up / page down to scroll in a buffer&lt;br /&gt;
&lt;br /&gt;
=== Customize themes ===&lt;br /&gt;
&lt;br /&gt;
==== Nerd Font and Powerline ====&lt;br /&gt;
&lt;br /&gt;
SSH server accepts the POWERLINE_SUPPORTED environment variable, so you can add in your own client shell configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
export POWERLINE_SUPPORTED=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then on the server, you can read it to apply a different theme for your prompt, tmux, etc. when you&#039;ve this variable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
if [[ -n &amp;quot;$POWERLINE_SUPPORTED&amp;quot; ]]; then&lt;br /&gt;
    source ~/.config/zsh/powerline-prompt.zsh&lt;br /&gt;
else&lt;br /&gt;
    source ~/.config/zsh/fallback-prompt.zsh&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We&#039;re open to other ideas to improve better integration with client themes.&lt;br /&gt;
If you save any setting in Operations repository, you can attach {{T|2361}} as a reference.&lt;br /&gt;
&lt;br /&gt;
== FreeBSD development ==&lt;br /&gt;
&lt;br /&gt;
For FreeBSD development tasks, WindRiver could be suitable if you need a amd64 arch.&lt;br /&gt;
It&#039;s certainly suitable for ports development, as Poudriere is installed there.&lt;br /&gt;
&lt;br /&gt;
=== Ports ===&lt;br /&gt;
==== Poudriere ====&lt;br /&gt;
The [https://docs.freebsd.org/en/books/porters-handbook/testing/#testing-poudriere FreeBSD porter handbook suggests Poudriere] as a convenient way to test ports in a clean environment.&lt;br /&gt;
&lt;br /&gt;
One of the big interest of this method is to start from scratch, allowing to detect hidden dependencies, like &amp;lt;code&amp;gt;pkg-config&amp;lt;/code&amp;gt; silently invoked by autotools.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Once time setup.&#039;&#039;&#039; Poudriere is installed in devserver role. To use it you need:&lt;br /&gt;
  - a FreeBSD ports tree, let&#039;s say you clone it to /home/luser/dev/freebsd/ports&lt;br /&gt;
  - to declare `luser-dev: /home/luser/dev/freebsd/ports` to {{Ops file|pillar/devserver/ports.sls}} under poudriere.ports&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Build a port.&#039;&#039;&#039; Then you can work in /home/luser/dev/freebsd/ports and build through:&lt;br /&gt;
&lt;br /&gt;
    sudo poudriere testport -j base14 -p luser_dev -o somecategory/someport&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Logs.&#039;&#039;&#039; You can browse logs on the web, as https://windriver.nasqueron.org/poudriere serves &amp;lt;code&amp;gt;/usr/local/poudriere/data/logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cache.&#039;&#039;&#039; To speed up recompilation, [https://ccache.dev/ ccache] is used as disk cache. Metrics for the cache are available for WindRiver at http://172.27.27.35:2337/metrics/ccache (curl it from the server).&lt;br /&gt;
&lt;br /&gt;
== Databases ==&lt;br /&gt;
&lt;br /&gt;
=== MariaDB ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-mysql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
You&#039;ve access to test_% databases, so you can without any specific configuration use that for unit tests. It&#039;s even OK to connect without login or password for the test_% namespace.&lt;br /&gt;
&lt;br /&gt;
If you need any other database and app config, you can request it on DevCentral.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20MariaDB%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
For long term projects, you can also specify where you want the credentials to be be provisioned,&lt;br /&gt;
or request a Vault AppRole for your application to fetch them securely.&lt;br /&gt;
&lt;br /&gt;
=== PostgreSQL ===&lt;br /&gt;
&lt;br /&gt;
Works on server with the &#039;&#039;devserver-pgsql&#039;&#039; role.&lt;br /&gt;
&lt;br /&gt;
{{Call for action&lt;br /&gt;
  | link = https://devcentral.nasqueron.org/maniphest/task/edit/form/1/?tags=servers,Nasqueron_Operations_Squad&amp;amp;title=Create%20PostgreSQL%20database%20on%20devserver&lt;br /&gt;
  | text = Request database creation&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Redis ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;For licensing purpose, should be replaced by Valkey or upgraded to a recent version under AGPLv3. See [https://devcentral.nasqueron.org/T1938 this discussion on DevCentral] for more context.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Redis is available on WindRiver, with database 0 used by NetBox.&lt;br /&gt;
&lt;br /&gt;
== General build information ==&lt;br /&gt;
=== Number of CPUS ===&lt;br /&gt;
On FreeBSD, the number of CPU core can be given by &amp;lt;code&amp;gt;sysctl hw.ncpu&amp;lt;/code&amp;gt; while the full XML description is available at &amp;lt;code&amp;gt;sysctl kern.sched.topology_spec&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Modern CPU allow for parallel compile tasks, use that.&lt;br /&gt;
&lt;br /&gt;
For example, as of October 2024, you can use 12 core to build on WindRiver.&lt;br /&gt;
&lt;br /&gt;
=== ccache ===&lt;br /&gt;
A compiler cache is installed with ccache.&lt;br /&gt;
&lt;br /&gt;
The /etc/make.conf configuration file defines /var/cache/ccache directory to be used for ports build.&lt;br /&gt;
That directory can&#039;t only be written as root.&lt;br /&gt;
&lt;br /&gt;
You can create your own configuration in &amp;lt;code&amp;gt;$HOME/.ccache/ccache.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
The exact path and configuration values can be get by &amp;lt;code&amp;gt;ccache -s&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Rust ==&lt;br /&gt;
=== Rustup ===&lt;br /&gt;
If you wish to have control of what stable and nightly Rust versions to use, you can use Rustup.&lt;br /&gt;
&lt;br /&gt;
To use it, the command &amp;lt;code&amp;gt;rustup-init&amp;lt;/code&amp;gt; is available as an alternative to the curl magic documented on the Rustup web site (actually it&#039;s a fairly recent version of that script saved locally).&lt;br /&gt;
&lt;br /&gt;
=== cargo, rustc ===&lt;br /&gt;
If you&#039;re OK with the Rust version installed by OS packages, feel free to call directly cargo or rustc.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
You can generate project documentation with the `cargo doc` command.&lt;br /&gt;
&lt;br /&gt;
== PHP ==&lt;br /&gt;
=== Tools provided ===&lt;br /&gt;
The following software are maintained as phar or repository clones in /opt:&lt;br /&gt;
* psysh, REPL shell to test PHP code (with PHP documentation available at /usr/local/share/psysh/php_manual.sqlite)&lt;br /&gt;
* psalm, for static analysis&lt;br /&gt;
* doctum and phpdoc (phpDocumentor) to generate documentation&lt;br /&gt;
* phpunit&lt;br /&gt;
* phpcs&lt;br /&gt;
&lt;br /&gt;
Most of those tools have wrappers in /usr/local/bin trying to detect if a version is installed locally for the PHP project through Composer and switching to that version if available.&lt;br /&gt;
&lt;br /&gt;
So by running &amp;quot;phpunit&amp;quot;, you&#039;ll run your Composer version and the system version when missing.&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
&lt;br /&gt;
To generate documentation for your code, &amp;lt;code&amp;gt;phpdoc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;doctum&amp;lt;/code&amp;gt; commands are available, including GraphViz and PlantUML for phpDocumentor classes graphes.&lt;br /&gt;
&lt;br /&gt;
== Network use ==&lt;br /&gt;
=== Port to use to spawn a server ===&lt;br /&gt;
To spawn a temporary web server, you can use ports 1080, 8000 or 8080 for a recommended 30 minutes maximum duration.&lt;br /&gt;
&lt;br /&gt;
For longer temporary need, the ports 3000, 3100, 3300, 3400, 3500, 3600, 3700, 3800 and 3900 are recommended.&lt;br /&gt;
&lt;br /&gt;
Other applications server can use any free port. Any long-term server should add an entry into {{Ops file|PORTS}}.&lt;br /&gt;
&lt;br /&gt;
== SSH tunnels ==&lt;br /&gt;
You can connect to the devserver and create one or more tunnels where you need:&lt;br /&gt;
* directly on the devserver: &amp;lt;code&amp;gt;ssh -L 3306:localhost:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
* to another server: &amp;lt;code&amp;gt;ssh -L 3307:db-B-001:3306 windriver.nasqueron.org&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Format is always -L &amp;lt;port on your computer&amp;gt;:&amp;lt;server ip or hostname&amp;gt;:&amp;lt;port on the server&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resources are reachable on your computer going to localhost and the port you specified locally (e.g. localhost:3307 for db-B-001 MariaDB).&lt;br /&gt;
&lt;br /&gt;
=== Certificates for private network resources ===&lt;br /&gt;
Resources using TLS connections on the private network use certificates managed by the CA we&#039;ve set up with Vault.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to install on your computer the root certificate: {{Ops file|roles/core/certificates/files/nasqueron-vault-ca.crt}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - Linux and UNIX&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Step 1. Install package !! Step 2. Drop .crt in directory !! Step 3. Run update command&lt;br /&gt;
|-&lt;br /&gt;
| Arch Linux || ca-certificates-utils || /etc/ca-certificates/trust-source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| Debian/Ubuntu|| ca-certificates || /usr/local/share/ca-certificates || update-ca-certificates&lt;br /&gt;
|-&lt;br /&gt;
| Fedora/RHEL || ca-certificates || /etc/pki/ca-trust/source/anchors || update-ca-trust&lt;br /&gt;
|-&lt;br /&gt;
| FreeBSD || ca_root_nss || /usr/local/share/certs || &#039;&#039;You&#039;re done, nothing to run :)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| openSUSE || ca-certificates-mozilla || /etc/pki/trust/anchors || update-ca-certificates&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Certificates installation procedure - OS using GUI&lt;br /&gt;
|-&lt;br /&gt;
! OS/distro || Procedure&lt;br /&gt;
|-&lt;br /&gt;
| ChromeOS || Chrome browser → Settings → Privacy and security → Security → Manage certificates → Authorities → Import&lt;br /&gt;
|-&lt;br /&gt;
| macOS || Keychain Access → System keychain → File &amp;gt; Import Items…&lt;br /&gt;
|-&lt;br /&gt;
| Windows || Import into “Trusted Root Certification Authorities” (mmc → Certificates snap-in)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Acceptable use policy ==&lt;br /&gt;
&amp;lt;poem&amp;gt;&lt;br /&gt;
&#039;&#039;The devserver is a shared space built on trust.&#039;&#039;&lt;br /&gt;
&#039;&#039;It exists to enable creativity, learning, and collaboration, not to enforce restrictive usage rules.&#039;&#039;&lt;br /&gt;
&amp;lt;/poem&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The devserver is primarily intended to:&lt;br /&gt;
&lt;br /&gt;
* support any activity related to Nasqueron;&lt;br /&gt;
* support contributions to open source software and free culture in general;&lt;br /&gt;
* support participation in technical workshops organized with Nasqueron.&lt;br /&gt;
&lt;br /&gt;
Any activity falling within these purposes is encouraged.&lt;br /&gt;
&lt;br /&gt;
The devserver may also be used to:&lt;br /&gt;
&lt;br /&gt;
* host personal content, provided resource usage remains reasonable (disk space, CPU, RAM, bandwidth);&lt;br /&gt;
* support activities aimed at learning, experimentation, and knowledge sharing.&lt;br /&gt;
&lt;br /&gt;
There is no requirement for all hosted content to be open source. Closed-source software, personal documents, or non-public projects are acceptable as long as they do not interfere with the normal operation of the service or other users.&lt;br /&gt;
&lt;br /&gt;
IRC usage should normally take place on Eglide.&lt;br /&gt;
However, hosting a lightweight IRC client (such as irssi or weechat) on another devserver, for example to avoid managing multiple SSH or tmux sessions, is acceptable.&lt;br /&gt;
&lt;br /&gt;
== Personal notes ==&lt;br /&gt;
&lt;br /&gt;
You can add in this section links to your own personal notes to use the devserver for your activities, including ideally a disaster recovery plan (how do you reinstall the code your work with?)&lt;br /&gt;
&lt;br /&gt;
* [[User:Dereckson/Devserver]]&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== You&#039;ve lost a file ===&lt;br /&gt;
&lt;br /&gt;
Your home directory has snapshots every 5 minutes, you can get back your recently lost file from a read-only snapshot:&lt;br /&gt;
&lt;br /&gt;
    cd ~/.zfs/snapshot&lt;br /&gt;
    ls&lt;br /&gt;
&lt;br /&gt;
Those are folders with your home directory content as it was at the snapshot time, you can even diff files to spot differences:&lt;br /&gt;
&lt;br /&gt;
    cd zfs-auto-snap_hourly-2023-06-05-20h00&lt;br /&gt;
    diff -u dev/nasqueron/operations/alkane/Jenkinsfile ~/dev/nasqueron/operations/alkane/Jenkinsfile&lt;br /&gt;
&lt;br /&gt;
You&#039;ve 12 months to act if your file is already in a monthly snapshot, but only 2 hours if it&#039;s in the last frequent snapshot and you keep modifying files, so act as quick as possible, ie right now. If you can&#039;t act right now, at least save the snapshot having the file (check that to avoid to save the wrong one) you want to recover with &amp;lt;code&amp;gt;zfs hold&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    zfs hold keep arcology/usr/home/dereckson@zfs-auto-snap_hourly-2023-06-05-22h00&lt;br /&gt;
&lt;br /&gt;
=== SSH issues ===&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve a message like &amp;lt;code&amp;gt;username@windriver.nasqueron.org: Permission denied (publickey).&amp;lt;/code&amp;gt;, it can means:&lt;br /&gt;
&lt;br /&gt;
* No SSH key has been offered&lt;br /&gt;
* SSH keys have been offered, but not recognized by the server&lt;br /&gt;
** Check {{Ops file|pillar/core/users.sls}} - do you have your public key on it?&lt;br /&gt;
** If not, and you have a new key, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add your new SSH key]&lt;br /&gt;
* SSH key is in a non-standard path - launch a SSH agent if needed (you&#039;ve already one with your desktop engine), then ssh-add &amp;lt;path to key&amp;gt;&lt;br /&gt;
* SSH key is protected by a passphrase and you don&#039;t remember the password&lt;br /&gt;
** Solution 1. Try to remember the password, with an agent and ssh-add, it will be less confusing as you&#039;ll work locally&lt;br /&gt;
** Solution 2. Destroy the SSH key, create a new one, [https://devcentral.nasqueron.org/maniphest/task/edit/form/3/ request to add that new key], login or ssh-add each day for one week to remember it&lt;br /&gt;
&lt;br /&gt;
[[Category:FreeBSD]]&lt;br /&gt;
[[Category:PHP]]&lt;br /&gt;
[[Category:ZFS]]&lt;br /&gt;
[[Category:Contributor guide]]&lt;br /&gt;
[[Category:Reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2601</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2601"/>
		<updated>2026-09-06T21:08:51Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Proofread&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
In addition to Salt, Nasqueron manages Infrastructure as Code using Terraform and OpenTofu. We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform.&lt;br /&gt;
&lt;br /&gt;
We provide a &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; wrapper that automatically runs OpenTofu or Terraform based on the directory and mounts the encrypted state directory on the fly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;It&#039;s important to work on Complector, so that all deployments use the shared Terraform state.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Paths ===&lt;br /&gt;
&lt;br /&gt;
Terraform states are stored on a local backend. To protect sensitive infrastructure state (which often contains plaintext secrets and topology data) at rest, we use a split-architecture approach combining [[Operations grimoire/ZFS|ZFS]] and [[Operations grimoire/PEFS|PEFS]] (Private Encrypted File System).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Paths for Terraform / OpenTofu deployment&lt;br /&gt;
|-&lt;br /&gt;
! Path&lt;br /&gt;
! Type&lt;br /&gt;
! Purpose&lt;br /&gt;
! Filesystem Properties&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations/terraform/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Standard Dir&lt;br /&gt;
| Terraform/OpenTofu declarative code&lt;br /&gt;
| Standard ZFS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform.enc&amp;lt;/code&amp;gt;&lt;br /&gt;
| ZFS Dataset&lt;br /&gt;
| Underlying encrypted block storage for PEFS&lt;br /&gt;
| &amp;lt;code&amp;gt;compression=zstd&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform/encrypted&amp;lt;/code&amp;gt;&lt;br /&gt;
| PEFS Mount&lt;br /&gt;
| &#039;&#039;&#039;Encrypted state files.&#039;&#039;&#039; Mounted only during TF operations.&lt;br /&gt;
| AES-256-XTS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform/tf-data&amp;lt;/code&amp;gt;&lt;br /&gt;
| ZFS Dataset&lt;br /&gt;
| &#039;&#039;&#039;Provider cache and modules files.&#039;&#039;&#039; &amp;lt;code&amp;gt;TF_DATA_DIR&amp;lt;/code&amp;gt; target.&lt;br /&gt;
| &amp;lt;code&amp;gt;compression=lz4&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;recordsize=128K&amp;lt;/code&amp;gt; (optimized for Go binaries)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Access control ===&lt;br /&gt;
&lt;br /&gt;
* PEFS operations (mounting, adding keys) are executed as the &amp;lt;code&amp;gt;deploy&amp;lt;/code&amp;gt; user.&lt;br /&gt;
* The wrapper verifies that the invoking user belongs to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;deploy&amp;lt;/code&amp;gt; group before proceeding.&lt;br /&gt;
* The PEFS passphrase is stored in HashiCorp Vault at &amp;lt;code&amp;gt;ops/infra/complector/terraform/pefs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Standard operations ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; wrapper automatically mounts PEFS, fetches the decryption key from Vault, executes the command, and unmounts PEFS upon exit, even on failure or &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can use it exactly like you would use terraform or tofu commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/ovh-ops-backups&lt;br /&gt;
$ tf init&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Maintenance of .tfstate files ===&lt;br /&gt;
For manual state surgery or moving files, use the explicit mount commands. These bypass the automatic unmount.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Mount PEFS and load keys for manual maintenance&lt;br /&gt;
tf mount&lt;br /&gt;
&lt;br /&gt;
# Perform manual operations (e.g., moving state files)&lt;br /&gt;
cd /opt/terraform/encrypted/tf-states&lt;br /&gt;
# ...&lt;br /&gt;
&lt;br /&gt;
# Unmount when finished&lt;br /&gt;
tf unmount&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Choose between opentofu or terraform ===&lt;br /&gt;
By default, tofu is used.&lt;br /&gt;
&lt;br /&gt;
The wrapper maintains an allowlist of projects that still require HashiCorp Terraform.&lt;br /&gt;
&lt;br /&gt;
The main blocker is &amp;lt;code&amp;gt;hashicorp/vault&amp;lt;/code&amp;gt;, not compiled for FreeBSD on OpenTofu registry.&lt;br /&gt;
&lt;br /&gt;
To add or remove a project from this list, edit the &amp;lt;code&amp;gt;STILL_REQUIRE_TERRAFORM&amp;lt;/code&amp;gt; variable in {{Ops file|roles/salt-primary/opentofu/files/tf.sh}}.&lt;br /&gt;
&lt;br /&gt;
=== New project configuration ===&lt;br /&gt;
&lt;br /&gt;
Every Terraform project must explicitly configure its local backend to write state into the PEFS-encrypted mount. &lt;br /&gt;
&lt;br /&gt;
Add this to your project&#039;s &amp;lt;code&amp;gt;backend.tf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;providers.tf&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;hcl&amp;quot;&amp;gt;&lt;br /&gt;
terraform {&lt;br /&gt;
  backend &amp;quot;local&amp;quot; {&lt;br /&gt;
    path = &amp;quot;/opt/terraform/encrypted/tf-states/&amp;lt;project-name&amp;gt;/terraform.tfstate&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: The provider cache path does not need to be configured in HCL. The wrapper automatically exports &amp;lt;code&amp;gt;TF_DATA_DIR=/opt/terraform/tf-data/&amp;lt;project-name&amp;gt;&amp;lt;/code&amp;gt;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-09-06, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ tf init # install any newly required providers and modules&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note that the benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole has been created in Vault, its credentials need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ tf init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important: Each time you reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This configuration is handled by Terraform.&lt;br /&gt;
&lt;br /&gt;
It requires full access to the public cloud project for backups, as the project is fully configured by these Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get the application key, application secret, and consumer key and write them in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate the file with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== First Terraform configuration (DRP) ==&lt;br /&gt;
=== Initialize Terraform working directories ===&lt;br /&gt;
Terraform and OpenTofu projects are currently deployed from Complector.&lt;br /&gt;
The states are encrypted, and a wrapper decrypts them for each operation.&lt;br /&gt;
&lt;br /&gt;
You need to restore the contents of /opt/terraform.enc from a backup, or if it is not available, initialize a new encrypted directory.&lt;br /&gt;
Currently, the procedure is specific to FreeBSD, as it requires PEFS to encrypt directories.&lt;br /&gt;
&lt;br /&gt;
# On the salt primary server, deploy the full roles/salt-primary, or at least the roles/salt-primary/opentofu unit&lt;br /&gt;
# Ensure pefs is active with &amp;lt;code&amp;gt;kldstat | grep pefs&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ensure Vault is up, then &amp;lt;code&amp;gt;vault kv put ops/infra/complector/terraform/pefs password=$(openssl rand -hex 48)&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run &amp;lt;code&amp;gt;initialize-pefs ops/infra/complector/terraform/pefs /opt/terraform.enc /opt/terraform/encrypted&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The previous version of the Vault secret, if still available, belongs to the previous directory; keep it if you need to restore that directory. However, you need a new password when creating a fresh encrypted directory.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tf init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tf plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tf init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some providers are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
To allow the wrapper to run &amp;lt;code&amp;gt;terraform&amp;lt;/code&amp;gt; commands, add your project directory to the &amp;lt;code&amp;gt;STILL_REQUIRE_TERRAFORM&amp;lt;/code&amp;gt; variable in {{Ops file|roles/salt-primary/opentofu/files/tf.sh}}.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tf state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2600</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2600"/>
		<updated>2026-09-06T18:54:32Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Per T2359. Explain the tf wrapper, and the directories where are stored the states/providers. Switch from tofu/terraform to tf command.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
In addition to Salt, Nasqueron manages Infrastructure as Code using Terraform and OpenTofu. We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform.&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
We provide a &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; wrapper that automatically runs OpenTofu or Terraform based on the directory and mounts the encrypted state directory on the fly.&lt;br /&gt;
&lt;br /&gt;
=== Paths ===&lt;br /&gt;
&lt;br /&gt;
Terraform states are stored on a local backend. To protect sensitive infrastructure state (which often contains plaintext secrets and topology data) at rest, we use a split-architecture approach combining [[Operations grimoire/ZFS|ZFS]] and [[Operations grimoire/PEFS|PEFS]] (Private Encrypted File System).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Paths for Terraform / OpenTofu deployment&lt;br /&gt;
|-&lt;br /&gt;
! Path&lt;br /&gt;
! Type&lt;br /&gt;
! Purpose&lt;br /&gt;
! Filesystem Properties&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations/terraform/&amp;lt;/code&amp;gt;&lt;br /&gt;
| Standard Dir&lt;br /&gt;
| Terraform/OpenTofu declarative code&lt;br /&gt;
| Standard ZFS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform.enc&amp;lt;/code&amp;gt;&lt;br /&gt;
| ZFS Dataset&lt;br /&gt;
| Underlying encrypted block storage for PEFS&lt;br /&gt;
| &amp;lt;code&amp;gt;compression=zstd&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform/encrypted&amp;lt;/code&amp;gt;&lt;br /&gt;
| PEFS Mount&lt;br /&gt;
| &#039;&#039;&#039;Encrypted state files.&#039;&#039;&#039; Mounted only during TF operations.&lt;br /&gt;
| AES-256-XTS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;/opt/terraform/tf-data&amp;lt;/code&amp;gt;&lt;br /&gt;
| ZFS Dataset&lt;br /&gt;
| &#039;&#039;&#039;Provider cache and modules.&#039;&#039;&#039; &amp;lt;code&amp;gt;TF_DATA_DIR&amp;lt;/code&amp;gt; target.&lt;br /&gt;
| &amp;lt;code&amp;gt;compression=lz4&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;recordsize=128K&amp;lt;/code&amp;gt; (optimized for Go binaries)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Access control ===&lt;br /&gt;
&lt;br /&gt;
* PEFS operations (mounting, adding keys) are executed as the &amp;lt;code&amp;gt;deploy&amp;lt;/code&amp;gt; user.&lt;br /&gt;
* The wrapper verifies the invoking user belongs to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;deploy&amp;lt;/code&amp;gt; group before proceeding.&lt;br /&gt;
* The PEFS passphrase to read the Terraform states encryption key is stored in HashiCorp Vault at &amp;lt;code&amp;gt;ops/infra/complector/terraform/pefs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Standard operations ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;tf&amp;lt;/code&amp;gt; wrapper automatically mounts PEFS, fetches the decryption key from Vault, executes the command, and unmounts PEFS upon exit, even on failure or &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can use it exactly like you would use terraform or tofu commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/ovh-ops-backups&lt;br /&gt;
$ tf init&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Maintenance of .tfstate files ===&lt;br /&gt;
For manual state surgery or moving files, use the explicit mount commands. These bypass the automatic unmount.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Mount PEFS and load keys for manual maintenance&lt;br /&gt;
tf mount&lt;br /&gt;
&lt;br /&gt;
# Perform manual operations (e.g., moving state files)&lt;br /&gt;
cd /opt/terraform/encrypted/tf-states&lt;br /&gt;
# ...&lt;br /&gt;
&lt;br /&gt;
# Unmount when finished&lt;br /&gt;
tf unmount&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Choose between opentofu or terraform ===&lt;br /&gt;
By default, tofu is used.&lt;br /&gt;
&lt;br /&gt;
The wrapper maintains an allowlist of project that still require the HashiCorp Terraform versions.&lt;br /&gt;
&lt;br /&gt;
The main blocker is &amp;lt;code&amp;gt;hashicorp/vault&amp;lt;/code&amp;gt;, not compiled for FreeBSD on OpenTofu registry.&lt;br /&gt;
&lt;br /&gt;
To add or remove a project there, edit the &amp;lt;code&amp;gt;STILL_REQUIRE_TERRAFORM&amp;lt;/code&amp;gt; variable in {{Ops file|roles/salt-primary/opentofu/files/tf.sh}}.&lt;br /&gt;
&lt;br /&gt;
=== New project configuration ===&lt;br /&gt;
&lt;br /&gt;
Every Terraform project must explicitly configure its local backend to write state into the PEFS-encrypted mount. &lt;br /&gt;
&lt;br /&gt;
Add this to your project&#039;s &amp;lt;code&amp;gt;backend.tf&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;providers.tf&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;hcl&amp;quot;&amp;gt;&lt;br /&gt;
terraform {&lt;br /&gt;
  backend &amp;quot;local&amp;quot; {&lt;br /&gt;
    path = &amp;quot;/opt/terraform/encrypted/tf-states/&amp;lt;project-name&amp;gt;/terraform.tfstate&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: The provider cache path does not need to be configured in HCL. The wrapper automatically exports &amp;lt;code&amp;gt;TF_DATA_DIR=/opt/terraform/tf-data/&amp;lt;project-name&amp;gt;&amp;lt;/code&amp;gt;.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-09-06, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ tf init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ tf init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ tf plan&lt;br /&gt;
$ tf apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This configuration is handled by Terraform.&lt;br /&gt;
&lt;br /&gt;
It requires a full access to public cloud project for backups, as the project is fully configured by those Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get application key, application secret and consumer_key in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate such files with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== First Terraform configuration (DRP) ==&lt;br /&gt;
=== Initialize Terraform working directories ===&lt;br /&gt;
Terraform and OpenTofu are currently deployed from Complector.&lt;br /&gt;
The working directories are encrypted, a wrapper decrypt them for each operation.&lt;br /&gt;
&lt;br /&gt;
You need to restore from a backup the content of /opt/terraform.enc or if not available reinitialize a new encrypted directory.&lt;br /&gt;
Currently, the procedure is valid for FreeBSD, as it requires PEFS to encrypt working director.&lt;br /&gt;
&lt;br /&gt;
# On the salt primary server, deploy the full roles/salt-primary, or at least the roles/salt-primary/opentofu unit&lt;br /&gt;
# Ensure pefs is active with &amp;lt;code&amp;gt;kldstat | grep pefs&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ensure Vault is up, then &amp;lt;code&amp;gt;vault kv put ops/infra/complector/terraform/pefs password=$(openssl rand -hex 48)&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run &amp;lt;code&amp;gt;initialize-pefs ops/infra/complector/terraform/pefs /opt/terraform.enc /opt/terraform/encrypted&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The vault previous version of the secret, if still available, is for the previous directory, keep it if we restore afterhand.&lt;br /&gt;
But you need a new password for your new fresh encrypted directory.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tf init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tf plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tf init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
To allow the wrapper to run &amp;lt;code&amp;gt;terraform&amp;lt;/code&amp;gt; commands, add your project directory to the &amp;lt;code&amp;gt;STILL_REQUIRE_TERRAFORM&amp;lt;/code&amp;gt; variable in {{Ops file|roles/salt-primary/opentofu/files/tf.sh}}.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tf state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2599</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2599"/>
		<updated>2026-09-06T13:50:17Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This configuration is handled by Terraform.&lt;br /&gt;
&lt;br /&gt;
It requires a full access to public cloud project for backups, as the project is fully configured by those Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get application key, application secret and consumer_key in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate such files with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenTofu states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
|-&lt;br /&gt;
| ovh-ops-backups || On disk || /opt/salt/nasqueron-operations/terraform/ovh-ops-backups/terraform.tfstate || Terraform&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== First Terraform configuration (DRP) ==&lt;br /&gt;
=== Initialize Terraform working directories ===&lt;br /&gt;
Terraform and OpenTofu are currently deployed from Complector.&lt;br /&gt;
The working directories are encrypted, a wrapper decrypt them for each operation.&lt;br /&gt;
&lt;br /&gt;
You need to restore from a backup the content of /opt/terraform.enc or if not available reinitialize a new encrypted directory.&lt;br /&gt;
Currently, the procedure is valid for FreeBSD, as it requires PEFS to encrypt working director.&lt;br /&gt;
&lt;br /&gt;
# On the salt primary server, deploy the full roles/salt-primary, or at least the roles/salt-primary/opentofu unit&lt;br /&gt;
# Ensure pefs is active with &amp;lt;code&amp;gt;kldstat | grep pefs&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ensure Vault is up, then &amp;lt;code&amp;gt;vault kv put ops/infra/complector/terraform/pefs password=$(openssl rand -hex 48)&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run &amp;lt;code&amp;gt;initialize-pefs ops/infra/complector/terraform/pefs /opt/terraform.enc /opt/terraform&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The vault previous version of the secret, if still available, is for the previous directory, keep it if we restore afterhand.&lt;br /&gt;
But you need a new password for your new fresh encrypted directory.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2598</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2598"/>
		<updated>2026-09-05T19:43:53Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* ovh-ops-backups */ Terraform, not OpenTofu&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This configuration is handled by Terraform.&lt;br /&gt;
&lt;br /&gt;
It requires a full access to public cloud project for backups, as the project is fully configured by those Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get application key, application secret and consumer_key in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate such files with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenTofu states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
|-&lt;br /&gt;
| ovh-ops-backups || On disk || /opt/salt/nasqueron-operations/terraform/ovh-ops-backups/terraform.tfstate || Terraform&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2597</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2597"/>
		<updated>2026-09-05T19:43:23Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Table of Terraform states */ Terraform too for ovh-ops-backups as we need to store credentials to Vault&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This Terraform configuration is handled by OpenTofu.&lt;br /&gt;
&lt;br /&gt;
It requires a full access to public cloud project for backups, as the project is fully configured by those Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get application key, application secret and consumer_key in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate such files with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenTofu states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
|-&lt;br /&gt;
| ovh-ops-backups || On disk || /opt/salt/nasqueron-operations/terraform/ovh-ops-backups/terraform.tfstate || Terraform&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2596</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2596"/>
		<updated>2026-09-05T18:42:44Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Table headers rows&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
  &lt;br /&gt;
  --nq-soft-sage: #eef1ea;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: var(--nq-soft-sage);&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The side rails contain navigation and contextual tools rather than reading&lt;br /&gt;
 * content. Deep Sea Dive gives those blocks one stable role in both modes.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(138, 197, 186, 0.38);&lt;br /&gt;
  background: var(--nq-dive);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 12px 28px rgba(32, 36, 45, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation #site-tools,&lt;br /&gt;
.skin-timeless #mw-related-navigation #catlinks-sidebar {&lt;br /&gt;
  background: #2f5459;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: rgba(138, 197, 186, 0.58);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:visited,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:visited {&lt;br /&gt;
  color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:focus-visible,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:focus-visible {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  text-decoration-color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .wikitable th,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-dive)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .wikitable th,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .mw_metadata th {&lt;br /&gt;
    background: var(--nq-dive)&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=MediaWiki:Common.css&amp;diff=2595</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=MediaWiki:Common.css&amp;diff=2595"/>
		<updated>2026-09-05T18:31:07Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: More complex alert solution&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Source code */&lt;br /&gt;
&lt;br /&gt;
code, tt, kbd, pre, samp {&lt;br /&gt;
	font-size: 0.9em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Call for actions button  */&lt;br /&gt;
&lt;br /&gt;
.btn {&lt;br /&gt;
	-webkit-border-radius: 6;&lt;br /&gt;
	-moz-border-radius: 6;&lt;br /&gt;
	border-radius: 6px;&lt;br /&gt;
	color: #ffffff;&lt;br /&gt;
	font-size: 1.4em;&lt;br /&gt;
	background: #4d7b94;&lt;br /&gt;
	padding: 10px 20px 10px 20px;&lt;br /&gt;
	text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.btn a, .btn a:hover, .btn a:visited {&lt;br /&gt;
	color: white !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.btn:hover {&lt;br /&gt;
	background: #6caacc;&lt;br /&gt;
	text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Inline code  */&lt;br /&gt;
&lt;br /&gt;
.inline-code, .inline-code div,  .inline-code pre {&lt;br /&gt;
	display: inline-table;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Technical Wiki Alert&lt;br /&gt;
   Aesthetic: industrial / editorial&lt;br /&gt;
   Typography: IBM Plex Sans + IBM Plex Mono&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
    /* Light mode */&lt;br /&gt;
    --alert-danger: #b42318;&lt;br /&gt;
    --alert-danger-dark: #7a1710;&lt;br /&gt;
    --alert-danger-bg: #fff4f1;&lt;br /&gt;
    --alert-danger-ink: #32110d;&lt;br /&gt;
    --alert-danger-line: #d8aaa3;&lt;br /&gt;
    --alert-accent: #f04438;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Base component&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
.alert {&lt;br /&gt;
    --alert-border: var(--alert-danger);&lt;br /&gt;
    --alert-bg: var(--alert-danger-bg);&lt;br /&gt;
    --alert-ink: var(--alert-danger-ink);&lt;br /&gt;
&lt;br /&gt;
    position: relative;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
&lt;br /&gt;
    margin: 1.5rem 0;&lt;br /&gt;
    padding: 1rem 1.25rem 1rem 1.5rem;&lt;br /&gt;
&lt;br /&gt;
    background:&lt;br /&gt;
        linear-gradient(&lt;br /&gt;
            90deg,&lt;br /&gt;
            color-mix(in srgb, var(--alert-border) 8%, transparent) 0,&lt;br /&gt;
            color-mix(in srgb, var(--alert-border) 8%, transparent) 4px,&lt;br /&gt;
            transparent 4px&lt;br /&gt;
        ),&lt;br /&gt;
        var(--alert-bg);&lt;br /&gt;
&lt;br /&gt;
    color: var(--alert-ink);&lt;br /&gt;
&lt;br /&gt;
    border: 1px solid var(--alert-danger-line);&lt;br /&gt;
    border-left: 5px solid var(--alert-border);&lt;br /&gt;
&lt;br /&gt;
    border-radius: 0;&lt;br /&gt;
&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Sans&amp;quot;, sans-serif;&lt;br /&gt;
    font-size: 0.95rem;&lt;br /&gt;
    font-weight: 400;&lt;br /&gt;
    line-height: 1.6;&lt;br /&gt;
&lt;br /&gt;
    box-shadow:&lt;br /&gt;
        4px 4px 0 rgba(50, 17, 13, 0.08);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Classification label */&lt;br /&gt;
&lt;br /&gt;
.alert::before {&lt;br /&gt;
    content: &amp;quot;ALERT&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    display: block;&lt;br /&gt;
    margin-bottom: 0.35rem;&lt;br /&gt;
&lt;br /&gt;
    color: var(--alert-danger);&lt;br /&gt;
&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Mono&amp;quot;, monospace;&lt;br /&gt;
    font-size: 0.68rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    letter-spacing: 0.14em;&lt;br /&gt;
    line-height: 1;&lt;br /&gt;
&lt;br /&gt;
    text-transform: uppercase;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Content */&lt;br /&gt;
&lt;br /&gt;
.alert strong:first-child {&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Links */&lt;br /&gt;
&lt;br /&gt;
.alert a {&lt;br /&gt;
    color: var(--alert-danger-dark);&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    text-decoration-thickness: 2px;&lt;br /&gt;
    text-underline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert a:hover {&lt;br /&gt;
    color: var(--alert-danger);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert a:focus-visible {&lt;br /&gt;
    outline: 2px solid var(--alert-accent);&lt;br /&gt;
    outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Dark mode — explicitly configured&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night {&lt;br /&gt;
    --alert-danger: #e5483e;&lt;br /&gt;
    --alert-danger-dark: #ff6b61;&lt;br /&gt;
    --alert-danger-bg: #211513;&lt;br /&gt;
    --alert-danger-ink: #f6e7e4;&lt;br /&gt;
    --alert-danger-line: #5c2b27;&lt;br /&gt;
    --alert-accent: #ff5a52;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert {&lt;br /&gt;
    box-shadow:&lt;br /&gt;
        4px 4px 0 rgba(0, 0, 0, 0.25);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Dark mode — follows system preference&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
    html.skin-theme-clientpref-os {&lt;br /&gt;
        --alert-danger: #e5483e;&lt;br /&gt;
        --alert-danger-dark: #ff6b61;&lt;br /&gt;
        --alert-danger-bg: #211513;&lt;br /&gt;
        --alert-danger-ink: #f6e7e4;&lt;br /&gt;
        --alert-danger-line: #5c2b27;&lt;br /&gt;
        --alert-accent: #ff5a52;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    html.skin-theme-clientpref-os .alert {&lt;br /&gt;
        box-shadow:&lt;br /&gt;
            4px 4px 0 rgba(0, 0, 0, 0.25);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Reduced motion&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: no-preference) {&lt;br /&gt;
    .alert {&lt;br /&gt;
        animation: alert-in 220ms ease-out both;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@keyframes alert-in {&lt;br /&gt;
    from {&lt;br /&gt;
        opacity: 0;&lt;br /&gt;
        transform: translateX(-8px);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    to {&lt;br /&gt;
        opacity: 1;&lt;br /&gt;
        transform: translateX(0);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Alert variants&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
.alert--warning {&lt;br /&gt;
    --alert-danger: #9a6700;&lt;br /&gt;
    --alert-danger-dark: #704800;&lt;br /&gt;
    --alert-bg: #fff9eb;&lt;br /&gt;
    --alert-ink: #3d2a0a;&lt;br /&gt;
    --alert-danger-line: #dec58e;&lt;br /&gt;
    --alert-accent: #d69e2e;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--info {&lt;br /&gt;
    --alert-danger: #1769aa;&lt;br /&gt;
    --alert-danger-dark: #0f4f82;&lt;br /&gt;
    --alert-bg: #eef7ff;&lt;br /&gt;
    --alert-ink: #102a43;&lt;br /&gt;
    --alert-danger-line: #a9cce8;&lt;br /&gt;
    --alert-accent: #2b8ed6;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--success {&lt;br /&gt;
    --alert-danger: #287a4b;&lt;br /&gt;
    --alert-danger-dark: #1b6038;&lt;br /&gt;
    --alert-bg: #eefbf3;&lt;br /&gt;
    --alert-ink: #102a1b;&lt;br /&gt;
    --alert-danger-line: #a8d4b8;&lt;br /&gt;
    --alert-accent: #38a169;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Dark variants */&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert--warning,&lt;br /&gt;
html.skin-theme-clientpref-os .alert--warning {&lt;br /&gt;
    --alert-danger: #d9a441;&lt;br /&gt;
    --alert-danger-dark: #f0c66b;&lt;br /&gt;
    --alert-bg: #211c11;&lt;br /&gt;
    --alert-ink: #f4ead5;&lt;br /&gt;
    --alert-danger-line: #5b4928;&lt;br /&gt;
    --alert-accent: #e5b14d;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert--info,&lt;br /&gt;
html.skin-theme-clientpref-os .alert--info {&lt;br /&gt;
    --alert-danger: #4da3df;&lt;br /&gt;
    --alert-danger-dark: #79c4f2;&lt;br /&gt;
    --alert-bg: #111d27;&lt;br /&gt;
    --alert-ink: #e1edf5;&lt;br /&gt;
    --alert-danger-line: #294b61;&lt;br /&gt;
    --alert-accent: #5db5ed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .alert--success,&lt;br /&gt;
html.skin-theme-clientpref-os .alert--success {&lt;br /&gt;
    --alert-danger: #55b87a;&lt;br /&gt;
    --alert-danger-dark: #7bd39a;&lt;br /&gt;
    --alert-bg: #112019;&lt;br /&gt;
    --alert-ink: #e1f0e6;&lt;br /&gt;
    --alert-danger-line: #28523a;&lt;br /&gt;
    --alert-accent: #62c889;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
   Variant labels&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
.alert--warning::before {&lt;br /&gt;
    content: &amp;quot;WARNING&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--info::before {&lt;br /&gt;
    content: &amp;quot;INFO&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert--success::before {&lt;br /&gt;
    content: &amp;quot;OK&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Colors from 2017 palette&lt;br /&gt;
    &lt;br /&gt;
    See https://devcentral.nasqueron.org/M11 or [[Design/2017 colors]]&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
.color-magnetic-one {&lt;br /&gt;
	/* Cyberspace gray */&lt;br /&gt;
	color: #44484D;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-two {&lt;br /&gt;
	/* Anchors Aweigh blue */&lt;br /&gt;
	color: #2B3441;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-three {&lt;br /&gt;
	/* Niagara blue — Pantone 17-4123 */&lt;br /&gt;
	color: #5587A2; &lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-four {&lt;br /&gt;
	/* Primrose yellow — Pantone 13-0755 */&lt;br /&gt;
	color: #F6D258;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-five {&lt;br /&gt;
	/* Green from http://www.elledecor.com/design-decorate/color/g3175/color-trends/ */&lt;br /&gt;
	color: #67947D;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Responsive blocks.&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
.block {&lt;br /&gt;
	margin: 0 0;&lt;br /&gt;
	padding: 5vh 5vw;&lt;br /&gt;
	&lt;br /&gt;
	border: #808080 solid 5px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col {&lt;br /&gt;
	position: relative;&lt;br /&gt;
	display:inline;&lt;br /&gt;
	float: left;&lt;br /&gt;
	margin-right: 1em;&lt;br /&gt;
	padding: 1vw;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col-quarter {&lt;br /&gt;
	width: 20%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col-half {&lt;br /&gt;
	width: 40%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block {&lt;br /&gt;
	min-height: 50vh;&lt;br /&gt;
	background-color: #44484D;&lt;br /&gt;
	color: white;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block dt {&lt;br /&gt;
    color: #67947D;&lt;br /&gt;
    font-family: &#039;Segoe UI&#039;, &#039;Segoe UI Emoji&#039;, &#039;Segoe UI Symbol&#039;, &#039;Lato&#039;, &#039;Liberation Sans&#039;, &#039;Noto Sans&#039;, &#039;Helvetica Neue&#039;, &#039;Helvetica&#039;, sans-serif;&lt;br /&gt;
    font-size: 1.375em;&lt;br /&gt;
    margin-bottom: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block a {&lt;br /&gt;
    color: #F6D258 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	Variable Grid System.&lt;br /&gt;
	Learn more ~ http://www.spry-soft.com/grids/&lt;br /&gt;
	Based on 960 Grid System - http://960.gs/&lt;br /&gt;
&lt;br /&gt;
	Licensed under GPL and MIT.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Containers&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
.container_12 {&lt;br /&gt;
	margin-left: auto;&lt;br /&gt;
	margin-right: auto;&lt;br /&gt;
	width: 960px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; Global&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.grid_1,&lt;br /&gt;
.grid_2,&lt;br /&gt;
.grid_3,&lt;br /&gt;
.grid_4,&lt;br /&gt;
.grid_5,&lt;br /&gt;
.grid_6,&lt;br /&gt;
.grid_7,&lt;br /&gt;
.grid_8,&lt;br /&gt;
.grid_9,&lt;br /&gt;
.grid_10,&lt;br /&gt;
.grid_11,&lt;br /&gt;
.grid_12 {&lt;br /&gt;
	display:inline;&lt;br /&gt;
	float: left;&lt;br /&gt;
	position: relative;&lt;br /&gt;
	margin-left: 10px;&lt;br /&gt;
	margin-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; Children (Alpha ~ First, Omega ~ Last)&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.alpha {&lt;br /&gt;
	margin-left: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.omega {&lt;br /&gt;
	margin-right: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_1 {&lt;br /&gt;
	width:60px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_2 {&lt;br /&gt;
	width:140px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_3 {&lt;br /&gt;
	width:220px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_4 {&lt;br /&gt;
	width:300px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_5 {&lt;br /&gt;
	width:380px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_6 {&lt;br /&gt;
	width:460px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_7 {&lt;br /&gt;
	width:540px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_8 {&lt;br /&gt;
	width:620px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_9 {&lt;br /&gt;
	width:700px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_10 {&lt;br /&gt;
	width:780px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_11 {&lt;br /&gt;
	width:860px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_12 {&lt;br /&gt;
	width:940px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Prefix Extra Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_1 {&lt;br /&gt;
	padding-left:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_2 {&lt;br /&gt;
	padding-left:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_3 {&lt;br /&gt;
	padding-left:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_4 {&lt;br /&gt;
	padding-left:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_5 {&lt;br /&gt;
	padding-left:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_6 {&lt;br /&gt;
	padding-left:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_7 {&lt;br /&gt;
	padding-left:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_8 {&lt;br /&gt;
	padding-left:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_9 {&lt;br /&gt;
	padding-left:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_10 {&lt;br /&gt;
	padding-left:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_11 {&lt;br /&gt;
	padding-left:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Suffix Extra Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_1 {&lt;br /&gt;
	padding-right:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_2 {&lt;br /&gt;
	padding-right:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_3 {&lt;br /&gt;
	padding-right:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_4 {&lt;br /&gt;
	padding-right:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_5 {&lt;br /&gt;
	padding-right:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_6 {&lt;br /&gt;
	padding-right:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_7 {&lt;br /&gt;
	padding-right:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_8 {&lt;br /&gt;
	padding-right:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_9 {&lt;br /&gt;
	padding-right:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_10 {&lt;br /&gt;
	padding-right:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_11 {&lt;br /&gt;
	padding-right:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Push Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_1 {&lt;br /&gt;
	left:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_2 {&lt;br /&gt;
	left:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_3 {&lt;br /&gt;
	left:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_4 {&lt;br /&gt;
	left:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_5 {&lt;br /&gt;
	left:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_6 {&lt;br /&gt;
	left:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_7 {&lt;br /&gt;
	left:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_8 {&lt;br /&gt;
	left:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_9 {&lt;br /&gt;
	left:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_10 {&lt;br /&gt;
	left:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_11 {&lt;br /&gt;
	left:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Pull Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_1 {&lt;br /&gt;
	left:-80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_2 {&lt;br /&gt;
	left:-160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_3 {&lt;br /&gt;
	left:-240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_4 {&lt;br /&gt;
	left:-320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_5 {&lt;br /&gt;
	left:-400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_6 {&lt;br /&gt;
	left:-480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_7 {&lt;br /&gt;
	left:-560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_8 {&lt;br /&gt;
	left:-640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_9 {&lt;br /&gt;
	left:-720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_10 {&lt;br /&gt;
	left:-800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_11 {&lt;br /&gt;
	left:-880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Clear Floated Elements&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* http://sonspring.com/journal/clearing-floats */&lt;br /&gt;
&lt;br /&gt;
.clear {&lt;br /&gt;
	clear: both;&lt;br /&gt;
	display: block;&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
	visibility: hidden;&lt;br /&gt;
	width: 0;&lt;br /&gt;
	height: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* http://perishablepress.com/press/2008/02/05/lessons-learned-concerning-the-clearfix-css-hack */&lt;br /&gt;
&lt;br /&gt;
.clearfix:after {&lt;br /&gt;
	clear: both;&lt;br /&gt;
	content: &#039; &#039;;&lt;br /&gt;
	display: block;&lt;br /&gt;
	font-size: 0;&lt;br /&gt;
	line-height: 0;&lt;br /&gt;
	visibility: hidden;&lt;br /&gt;
	width: 0;&lt;br /&gt;
	height: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.clearfix {&lt;br /&gt;
	display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
* html .clearfix {&lt;br /&gt;
	height: 1%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.clearfix {&lt;br /&gt;
	display: block;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2594</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2594"/>
		<updated>2026-09-05T18:24:14Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Table of Terraform states */ row break&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This Terraform configuration is handled by OpenTofu.&lt;br /&gt;
&lt;br /&gt;
It requires a full access to public cloud project for backups, as the project is fully configured by those Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get application key, application secret and consumer_key in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate such files with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenTofu states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
|-&lt;br /&gt;
| ovh-ops-backups || On disk || /opt/salt/nasqueron-operations/terraform/ovh-ops-backups/terraform.tfstate || OpenTofu&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2593</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2593"/>
		<updated>2026-09-05T18:19:55Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Specific deployment notes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== CARP OVH secrets usage ====&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
=== ovh-ops-backups ===&lt;br /&gt;
This Terraform configuration is handled by OpenTofu.&lt;br /&gt;
&lt;br /&gt;
It requires a full access to public cloud project for backups, as the project is fully configured by those Terraform files.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to get application key, application secret and consumer_key in your $HOME/.ovh.conf on Complector.&lt;br /&gt;
You can also generate such files with &amp;lt;code&amp;gt;ovhcloud login&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenTofu states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
| ovh-ops-backups || On disk || /opt/salt/nasqueron-operations/terraform/ovh-ops-backups/terraform.tfstate || OpenTofu&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=MediaWiki:Common.css&amp;diff=2592</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=MediaWiki:Common.css&amp;diff=2592"/>
		<updated>2026-09-05T18:15:08Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Improve alert CSS code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Source code */&lt;br /&gt;
&lt;br /&gt;
code, tt, kbd, pre, samp {&lt;br /&gt;
	font-size: 0.9em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Call for actions button  */&lt;br /&gt;
&lt;br /&gt;
.btn {&lt;br /&gt;
	-webkit-border-radius: 6;&lt;br /&gt;
	-moz-border-radius: 6;&lt;br /&gt;
	border-radius: 6px;&lt;br /&gt;
	color: #ffffff;&lt;br /&gt;
	font-size: 1.4em;&lt;br /&gt;
	background: #4d7b94;&lt;br /&gt;
	padding: 10px 20px 10px 20px;&lt;br /&gt;
	text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.btn a, .btn a:hover, .btn a:visited {&lt;br /&gt;
	color: white !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.btn:hover {&lt;br /&gt;
	background: #6caacc;&lt;br /&gt;
	text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Inline code  */&lt;br /&gt;
&lt;br /&gt;
.inline-code, .inline-code div,  .inline-code pre {&lt;br /&gt;
	display: inline-table;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Banners&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
    --alert-danger: #b42318;&lt;br /&gt;
    --alert-danger-dark: #7a1710;&lt;br /&gt;
    --alert-danger-bg: #fff4f1;&lt;br /&gt;
    --alert-danger-ink: #32110d;&lt;br /&gt;
    --alert-danger-line: #d8aaa3;&lt;br /&gt;
    --alert-accent: #f04438;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Technical wiki alert&lt;br /&gt;
 * Direction: industrial / editorial&lt;br /&gt;
 */&lt;br /&gt;
.alert {&lt;br /&gt;
    --alert-border: var(--alert-danger);&lt;br /&gt;
    --alert-bg: var(--alert-danger-bg);&lt;br /&gt;
    --alert-ink: var(--alert-danger-ink);&lt;br /&gt;
&lt;br /&gt;
    position: relative;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
&lt;br /&gt;
    margin: 1.5rem 0;&lt;br /&gt;
    padding: 1rem 1.25rem 1rem 1.5rem;&lt;br /&gt;
&lt;br /&gt;
    background:&lt;br /&gt;
        linear-gradient(&lt;br /&gt;
            90deg,&lt;br /&gt;
            rgba(180, 35, 24, 0.055) 0,&lt;br /&gt;
            rgba(180, 35, 24, 0.055) 4px,&lt;br /&gt;
            transparent 4px&lt;br /&gt;
        ),&lt;br /&gt;
        var(--alert-bg);&lt;br /&gt;
&lt;br /&gt;
    color: var(--alert-ink);&lt;br /&gt;
    border: 1px solid var(--alert-danger-line);&lt;br /&gt;
    border-left: 5px solid var(--alert-border);&lt;br /&gt;
&lt;br /&gt;
    border-radius: 0;&lt;br /&gt;
&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Sans&amp;quot;, sans-serif;&lt;br /&gt;
    font-size: 0.95rem;&lt;br /&gt;
    line-height: 1.6;&lt;br /&gt;
&lt;br /&gt;
    box-shadow:&lt;br /&gt;
        4px 4px 0 rgba(50, 17, 13, 0.08);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Classification stamp */&lt;br /&gt;
.alert::before {&lt;br /&gt;
    content: &amp;quot;ALERT&amp;quot;;&lt;br /&gt;
    display: block;&lt;br /&gt;
&lt;br /&gt;
    margin-bottom: 0.35rem;&lt;br /&gt;
&lt;br /&gt;
    color: var(--alert-danger);&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Mono&amp;quot;, monospace;&lt;br /&gt;
    font-size: 0.68rem;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    letter-spacing: 0.14em;&lt;br /&gt;
    line-height: 1;&lt;br /&gt;
&lt;br /&gt;
    text-transform: uppercase;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Optional heading */&lt;br /&gt;
.alert strong:first-child {&lt;br /&gt;
    font-family: &amp;quot;IBM Plex Sans&amp;quot;, sans-serif;&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Links should look intentional, not like default wiki links */&lt;br /&gt;
.alert a {&lt;br /&gt;
    color: var(--alert-danger-dark);&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    text-decoration-thickness: 2px;&lt;br /&gt;
    text-underline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.alert a:hover {&lt;br /&gt;
    color: #4f0d08;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Keyboard accessibility */&lt;br /&gt;
.alert a:focus-visible {&lt;br /&gt;
    outline: 2px solid var(--alert-accent);&lt;br /&gt;
    outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Respect reduced-motion preferences if you add the reveal below */&lt;br /&gt;
@media (prefers-reduced-motion: no-preference) {&lt;br /&gt;
    .alert {&lt;br /&gt;
        animation: alert-in 220ms ease-out both;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@keyframes alert-in {&lt;br /&gt;
    from {&lt;br /&gt;
        opacity: 0;&lt;br /&gt;
        transform: translateX(-8px);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    to {&lt;br /&gt;
        opacity: 1;&lt;br /&gt;
        transform: translateX(0);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Colors from 2017 palette&lt;br /&gt;
    &lt;br /&gt;
    See https://devcentral.nasqueron.org/M11 or [[Design/2017 colors]]&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
.color-magnetic-one {&lt;br /&gt;
	/* Cyberspace gray */&lt;br /&gt;
	color: #44484D;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-two {&lt;br /&gt;
	/* Anchors Aweigh blue */&lt;br /&gt;
	color: #2B3441;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-three {&lt;br /&gt;
	/* Niagara blue — Pantone 17-4123 */&lt;br /&gt;
	color: #5587A2; &lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-four {&lt;br /&gt;
	/* Primrose yellow — Pantone 13-0755 */&lt;br /&gt;
	color: #F6D258;&lt;br /&gt;
}&lt;br /&gt;
.color-magnetic-five {&lt;br /&gt;
	/* Green from http://www.elledecor.com/design-decorate/color/g3175/color-trends/ */&lt;br /&gt;
	color: #67947D;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Responsive blocks.&lt;br /&gt;
 */&lt;br /&gt;
 &lt;br /&gt;
.block {&lt;br /&gt;
	margin: 0 0;&lt;br /&gt;
	padding: 5vh 5vw;&lt;br /&gt;
	&lt;br /&gt;
	border: #808080 solid 5px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col {&lt;br /&gt;
	position: relative;&lt;br /&gt;
	display:inline;&lt;br /&gt;
	float: left;&lt;br /&gt;
	margin-right: 1em;&lt;br /&gt;
	padding: 1vw;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col-quarter {&lt;br /&gt;
	width: 20%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.block-col-half {&lt;br /&gt;
	width: 40%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block {&lt;br /&gt;
	min-height: 50vh;&lt;br /&gt;
	background-color: #44484D;&lt;br /&gt;
	color: white;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block dt {&lt;br /&gt;
    color: #67947D;&lt;br /&gt;
    font-family: &#039;Segoe UI&#039;, &#039;Segoe UI Emoji&#039;, &#039;Segoe UI Symbol&#039;, &#039;Lato&#039;, &#039;Liberation Sans&#039;, &#039;Noto Sans&#039;, &#039;Helvetica Neue&#039;, &#039;Helvetica&#039;, sans-serif;&lt;br /&gt;
    font-size: 1.375em;&lt;br /&gt;
    margin-bottom: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.user-block a {&lt;br /&gt;
    color: #F6D258 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	Variable Grid System.&lt;br /&gt;
	Learn more ~ http://www.spry-soft.com/grids/&lt;br /&gt;
	Based on 960 Grid System - http://960.gs/&lt;br /&gt;
&lt;br /&gt;
	Licensed under GPL and MIT.&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Containers&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
.container_12 {&lt;br /&gt;
	margin-left: auto;&lt;br /&gt;
	margin-right: auto;&lt;br /&gt;
	width: 960px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; Global&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.grid_1,&lt;br /&gt;
.grid_2,&lt;br /&gt;
.grid_3,&lt;br /&gt;
.grid_4,&lt;br /&gt;
.grid_5,&lt;br /&gt;
.grid_6,&lt;br /&gt;
.grid_7,&lt;br /&gt;
.grid_8,&lt;br /&gt;
.grid_9,&lt;br /&gt;
.grid_10,&lt;br /&gt;
.grid_11,&lt;br /&gt;
.grid_12 {&lt;br /&gt;
	display:inline;&lt;br /&gt;
	float: left;&lt;br /&gt;
	position: relative;&lt;br /&gt;
	margin-left: 10px;&lt;br /&gt;
	margin-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; Children (Alpha ~ First, Omega ~ Last)&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.alpha {&lt;br /&gt;
	margin-left: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.omega {&lt;br /&gt;
	margin-right: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Grid &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_1 {&lt;br /&gt;
	width:60px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_2 {&lt;br /&gt;
	width:140px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_3 {&lt;br /&gt;
	width:220px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_4 {&lt;br /&gt;
	width:300px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_5 {&lt;br /&gt;
	width:380px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_6 {&lt;br /&gt;
	width:460px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_7 {&lt;br /&gt;
	width:540px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_8 {&lt;br /&gt;
	width:620px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_9 {&lt;br /&gt;
	width:700px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_10 {&lt;br /&gt;
	width:780px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_11 {&lt;br /&gt;
	width:860px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .grid_12 {&lt;br /&gt;
	width:940px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Prefix Extra Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_1 {&lt;br /&gt;
	padding-left:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_2 {&lt;br /&gt;
	padding-left:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_3 {&lt;br /&gt;
	padding-left:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_4 {&lt;br /&gt;
	padding-left:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_5 {&lt;br /&gt;
	padding-left:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_6 {&lt;br /&gt;
	padding-left:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_7 {&lt;br /&gt;
	padding-left:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_8 {&lt;br /&gt;
	padding-left:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_9 {&lt;br /&gt;
	padding-left:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_10 {&lt;br /&gt;
	padding-left:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .prefix_11 {&lt;br /&gt;
	padding-left:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Suffix Extra Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_1 {&lt;br /&gt;
	padding-right:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_2 {&lt;br /&gt;
	padding-right:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_3 {&lt;br /&gt;
	padding-right:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_4 {&lt;br /&gt;
	padding-right:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_5 {&lt;br /&gt;
	padding-right:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_6 {&lt;br /&gt;
	padding-right:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_7 {&lt;br /&gt;
	padding-right:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_8 {&lt;br /&gt;
	padding-right:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_9 {&lt;br /&gt;
	padding-right:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_10 {&lt;br /&gt;
	padding-right:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .suffix_11 {&lt;br /&gt;
	padding-right:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Push Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_1 {&lt;br /&gt;
	left:80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_2 {&lt;br /&gt;
	left:160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_3 {&lt;br /&gt;
	left:240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_4 {&lt;br /&gt;
	left:320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_5 {&lt;br /&gt;
	left:400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_6 {&lt;br /&gt;
	left:480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_7 {&lt;br /&gt;
	left:560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_8 {&lt;br /&gt;
	left:640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_9 {&lt;br /&gt;
	left:720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_10 {&lt;br /&gt;
	left:800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .push_11 {&lt;br /&gt;
	left:880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Pull Space &amp;gt;&amp;gt; 12 Columns&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_1 {&lt;br /&gt;
	left:-80px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_2 {&lt;br /&gt;
	left:-160px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_3 {&lt;br /&gt;
	left:-240px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_4 {&lt;br /&gt;
	left:-320px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_5 {&lt;br /&gt;
	left:-400px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_6 {&lt;br /&gt;
	left:-480px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_7 {&lt;br /&gt;
	left:-560px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_8 {&lt;br /&gt;
	left:-640px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_9 {&lt;br /&gt;
	left:-720px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_10 {&lt;br /&gt;
	left:-800px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.container_12 .pull_11 {&lt;br /&gt;
	left:-880px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Clear Floated Elements&lt;br /&gt;
----------------------------------------------------------------------------------------------------*/&lt;br /&gt;
&lt;br /&gt;
/* http://sonspring.com/journal/clearing-floats */&lt;br /&gt;
&lt;br /&gt;
.clear {&lt;br /&gt;
	clear: both;&lt;br /&gt;
	display: block;&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
	visibility: hidden;&lt;br /&gt;
	width: 0;&lt;br /&gt;
	height: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* http://perishablepress.com/press/2008/02/05/lessons-learned-concerning-the-clearfix-css-hack */&lt;br /&gt;
&lt;br /&gt;
.clearfix:after {&lt;br /&gt;
	clear: both;&lt;br /&gt;
	content: &#039; &#039;;&lt;br /&gt;
	display: block;&lt;br /&gt;
	font-size: 0;&lt;br /&gt;
	line-height: 0;&lt;br /&gt;
	visibility: hidden;&lt;br /&gt;
	width: 0;&lt;br /&gt;
	height: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.clearfix {&lt;br /&gt;
	display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
* html .clearfix {&lt;br /&gt;
	height: 1%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.clearfix {&lt;br /&gt;
	display: block;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2591</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2591"/>
		<updated>2026-09-05T18:04:37Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Table of Terraform states */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CARP OVH secrets usage ===&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenTofu states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
| ovh-ops-backups || On disk || /opt/salt/nasqueron-operations/terraform/ovh-ops-backups/terraform.tfstate || OpenTofu&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2590</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2590"/>
		<updated>2026-08-29T16:08:12Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Introducing soft sage color #eef1ea for out-of-page content (e.g. left / right menu)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
  &lt;br /&gt;
  --nq-soft-sage: #eef1ea;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: var(--nq-soft-sage);&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The side rails contain navigation and contextual tools rather than reading&lt;br /&gt;
 * content. Deep Sea Dive gives those blocks one stable role in both modes.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(138, 197, 186, 0.38);&lt;br /&gt;
  background: var(--nq-dive);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 12px 28px rgba(32, 36, 45, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation #site-tools,&lt;br /&gt;
.skin-timeless #mw-related-navigation #catlinks-sidebar {&lt;br /&gt;
  background: #2f5459;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: rgba(138, 197, 186, 0.58);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:visited,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:visited {&lt;br /&gt;
  color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:focus-visible,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:focus-visible {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  text-decoration-color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2589</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2589"/>
		<updated>2026-08-29T15:36:11Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Undid revision 2588 by Dereckson (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The side rails contain navigation and contextual tools rather than reading&lt;br /&gt;
 * content. Deep Sea Dive gives those blocks one stable role in both modes.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(138, 197, 186, 0.38);&lt;br /&gt;
  background: var(--nq-dive);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 12px 28px rgba(32, 36, 45, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation #site-tools,&lt;br /&gt;
.skin-timeless #mw-related-navigation #catlinks-sidebar {&lt;br /&gt;
  background: #2f5459;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: rgba(138, 197, 186, 0.58);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:visited,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:visited {&lt;br /&gt;
  color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:focus-visible,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:focus-visible {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  text-decoration-color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2588</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2588"/>
		<updated>2026-08-29T15:35:06Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Qwen older experiment&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* === Nasqueron Timeless Skin Theme === */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
    /* Palette Tokens */&lt;br /&gt;
    --nk-mountain-fig: #383C49;&lt;br /&gt;
    --nk-deep-sea: #376167;&lt;br /&gt;
    --nk-stolen-kiss: #813235;&lt;br /&gt;
    --nk-little-blue: #8AC5BA;&lt;br /&gt;
    &lt;br /&gt;
    /* Light Mode Defaults */&lt;br /&gt;
    --nk-grenadine: #C2555D;&lt;br /&gt;
    --bg-primary: #F9F9F7;&lt;br /&gt;
    --bg-surface: #FFFFFF;&lt;br /&gt;
    --text-primary: var(--nk-mountain-fig);&lt;br /&gt;
    --text-secondary: #5A6072;&lt;br /&gt;
    --accent-primary: var(--nk-grenadine);&lt;br /&gt;
    --border-color: #E5E5E2;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
    :root {&lt;br /&gt;
        --nk-grenadine: #E5868E;&lt;br /&gt;
        --bg-primary: #242730;&lt;br /&gt;
        --bg-surface: #383C49;&lt;br /&gt;
        --text-primary: #F2F4F5;&lt;br /&gt;
        --text-secondary: #A9B0C2;&lt;br /&gt;
        --accent-primary: var(--nk-grenadine);&lt;br /&gt;
        --border-color: #4B5162;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Global Typography &amp;amp; Reset */&lt;br /&gt;
body, #mw-wrapper {&lt;br /&gt;
    background-color: var(--bg-primary) !important;&lt;br /&gt;
    color: var(--text-primary);&lt;br /&gt;
    font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, sans-serif;&lt;br /&gt;
    -webkit-font-smoothing: antialiased;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Content Area */&lt;br /&gt;
.mw-body, .parsoid-body {&lt;br /&gt;
    background-color: var(--bg-surface) !important;&lt;br /&gt;
    color: var(--text-primary);&lt;br /&gt;
    border: 1px solid var(--border-color);&lt;br /&gt;
    border-radius: 8px;&lt;br /&gt;
    box-shadow: 0 4px 12px rgba(0,0,0,0.02);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Headings (Typographic Hierarchy) */&lt;br /&gt;
.mw-body h1, .mw-body h2, .mw-heading1, .mw-heading2 {&lt;br /&gt;
    color: var(--nk-deep-sea) !important;&lt;br /&gt;
    border-bottom: 1px solid var(--border-color);&lt;br /&gt;
    font-weight: 700;&lt;br /&gt;
    letter-spacing: -0.01em;&lt;br /&gt;
}&lt;br /&gt;
.mw-body h3, .mw-body h4 {&lt;br /&gt;
    color: var(--text-primary) !important;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Links &amp;amp; Interactivity */&lt;br /&gt;
a { color: var(--accent-primary) !important; text-decoration: none; transition: color 0.2s ease; }&lt;br /&gt;
a:hover { color: var(--nk-stolen-kiss) !important; text-decoration: underline; }&lt;br /&gt;
a.new { color: #B22222 !important; } /* Uncreated pages */&lt;br /&gt;
&lt;br /&gt;
/* Navigation &amp;amp; Sidebars */&lt;br /&gt;
#site-navigation, #other-languages, #page-tools {&lt;br /&gt;
    background-color: var(--bg-surface);&lt;br /&gt;
    border-right: 1px solid var(--border-color);&lt;br /&gt;
}&lt;br /&gt;
#site-navigation h3, #page-tools h3 {&lt;br /&gt;
    color: var(--text-secondary);&lt;br /&gt;
    font-size: 0.75rem;&lt;br /&gt;
    text-transform: uppercase;&lt;br /&gt;
    letter-spacing: 0.1em;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Top Personal Tools &amp;amp; Search */&lt;br /&gt;
#personal {&lt;br /&gt;
    background-color: var(--bg-surface) !important;&lt;br /&gt;
    border-bottom: 1px solid var(--border-color);&lt;br /&gt;
}&lt;br /&gt;
#simpleSearch {&lt;br /&gt;
    background-color: var(--bg-primary) !important;&lt;br /&gt;
    border: 1px solid var(--border-color);&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
}&lt;br /&gt;
#simpleSearch #searchInput {&lt;br /&gt;
    color: var(--text-primary);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Buttons (Material Honesty) */&lt;br /&gt;
.mw-ui-button, .mw-ui-button.mw-ui-progressive {&lt;br /&gt;
    background-color: var(--accent-primary) !important;&lt;br /&gt;
    border: 1px solid var(--accent-primary) !important;&lt;br /&gt;
    color: #FFFFFF !important;&lt;br /&gt;
    border-radius: 6px;&lt;br /&gt;
    font-weight: 600;&lt;br /&gt;
    transition: all 0.2s ease;&lt;br /&gt;
    box-shadow: 0 2px 4px rgba(194, 85, 93, 0.1);&lt;br /&gt;
}&lt;br /&gt;
.mw-ui-button:hover {&lt;br /&gt;
    background-color: var(--nk-stolen-kiss) !important;&lt;br /&gt;
    border-color: var(--nk-stolen-kiss) !important;&lt;br /&gt;
    box-shadow: 0 4px 8px rgba(129, 50, 53, 0.2);&lt;br /&gt;
}&lt;br /&gt;
.mw-ui-button:active {&lt;br /&gt;
    transform: translateY(1px);&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2587</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2587"/>
		<updated>2026-08-29T15:08:13Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Invert logo color with filter&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The side rails contain navigation and contextual tools rather than reading&lt;br /&gt;
 * content. Deep Sea Dive gives those blocks one stable role in both modes.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(138, 197, 186, 0.38);&lt;br /&gt;
  background: var(--nq-dive);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 12px 28px rgba(32, 36, 45, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation #site-tools,&lt;br /&gt;
.skin-timeless #mw-related-navigation #catlinks-sidebar {&lt;br /&gt;
  background: #2f5459;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: rgba(138, 197, 186, 0.58);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:visited,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:visited {&lt;br /&gt;
  color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:focus-visible,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:focus-visible {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  text-decoration-color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .mw-wiki-logo img,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .mw-wiki-logo img {&lt;br /&gt;
    filter: invert(1);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2586</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2586"/>
		<updated>2026-08-29T10:44:20Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Left bar too&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The side rails contain navigation and contextual tools rather than reading&lt;br /&gt;
 * content. Deep Sea Dive gives those blocks one stable role in both modes.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(138, 197, 186, 0.38);&lt;br /&gt;
  background: var(--nq-dive);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 12px 28px rgba(32, 36, 45, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation #site-tools,&lt;br /&gt;
.skin-timeless #mw-related-navigation #catlinks-sidebar {&lt;br /&gt;
  background: #2f5459;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: rgba(138, 197, 186, 0.58);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:visited,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:visited {&lt;br /&gt;
  color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-site-navigation .sidebar-chunk a:focus-visible,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:focus-visible {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  text-decoration-color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2585</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2585"/>
		<updated>2026-08-29T10:36:43Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Right tools blocks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The right rail contains contextual tools rather than reading content. A&lt;br /&gt;
 * Deep Sea Dive surface gives those blocks a distinct, stable role in both&lt;br /&gt;
 * light and dark modes without changing the quieter left navigation.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(138, 197, 186, 0.38);&lt;br /&gt;
  background: var(--nq-dive);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 12px 28px rgba(32, 36, 45, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-related-navigation #catlinks-sidebar {&lt;br /&gt;
  background: #2f5459;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: rgba(138, 197, 186, 0.58);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:visited {&lt;br /&gt;
  color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:hover,&lt;br /&gt;
.skin-timeless #mw-related-navigation .sidebar-chunk a:focus-visible {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  text-decoration-color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2584</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2584"/>
		<updated>2026-08-29T10:25:14Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Update to support personal tools bar&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Timeless renders the personal tools inside the header, but the dropdown is&lt;br /&gt;
 * a light floating surface. Restate its colours here so the broad header link&lt;br /&gt;
 * rule above does not produce paper-coloured text on a paper background.&lt;br /&gt;
 */&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  box-shadow: 0 14px 34px rgba(56, 60, 73, 0.18);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: var(--nq-surface);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: var(--nq-line);&lt;br /&gt;
  color: rgba(56, 60, 73, 0.68);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  margin-top: 0.35em;&lt;br /&gt;
  padding-top: 0.35em;&lt;br /&gt;
  border-top: 1px solid var(--nq-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
  border-bottom-color: #292e38;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
  border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
  background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.34);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::before,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::before {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner::after,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner::after {&lt;br /&gt;
    border-bottom-color: #292e38;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label {&lt;br /&gt;
    border-bottom-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
    color: rgba(247, 244, 236, 0.66);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #p-personal-label b,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #p-personal-label b {&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner a:visited,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner a:visited {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:hover,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner .mw-list-item a:focus-visible {&lt;br /&gt;
    background: rgba(138, 197, 186, 0.14);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-header-container #personal-inner #pt-logout,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-header-container #personal-inner #pt-logout {&lt;br /&gt;
    border-top-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=MediaWiki_SaaS&amp;diff=2583</id>
		<title>MediaWiki SaaS</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=MediaWiki_SaaS&amp;diff=2583"/>
		<updated>2026-08-29T07:47:04Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Upgrade&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We maintain an up-to-date MediaWiki installation to serve this wiki, and several others.&lt;br /&gt;
&lt;br /&gt;
If you license your content under an open source license like CC-BY, you&#039;re welcome to request hosting.&lt;br /&gt;
&lt;br /&gt;
== Hosted wikis ==&lt;br /&gt;
&lt;br /&gt;
The following wikis are hosted on this service:&lt;br /&gt;
&lt;br /&gt;
* https://agora.nasqueron.org/ ­— this wiki&lt;br /&gt;
* https://www.wolfplex.org/wiki/ — Wolfplex Hackerspace wiki&lt;br /&gt;
&lt;br /&gt;
== Repositories ==&lt;br /&gt;
&lt;br /&gt;
* [https://devcentral.nasqueron.org/source/saas-mediawiki saas-mediawiki]: MediaWiki SaaS entry point: contains the wiki configuration and the MediaWiki bootstrap logic&lt;br /&gt;
* [https://devcentral.nasqueron.org/source/saas-service saas-service]: base entry point for SaaS: serves /status requests and contains some base classes&lt;br /&gt;
* [https://devcentral.nasqueron.org/source/operations rOPS]: describes the saas-mediawiki role provisioning instructions&lt;br /&gt;
&lt;br /&gt;
== How to add a wiki? ==&lt;br /&gt;
&lt;br /&gt;
# DNS: &amp;lt;your domain&amp;gt; CNAME saas-mediawiki.nasqueron.org&lt;br /&gt;
# Send a change to the Operations repository&lt;br /&gt;
## nginx: add your server block to the webserver-legacy role, you need to use &amp;lt;code&amp;gt;include includes/mediawiki-root&amp;lt;/code&amp;gt; to serve /Article pages, or &amp;lt;code&amp;gt;include includes/mediawiki-wiki&amp;lt;/code&amp;gt; to serve /wiki/Article pages (useful if you&#039;ve other services than a wiki on this domain)&lt;br /&gt;
## data directory: add your canonical host to the pillar/saas/mediawiki.sls file in the mediawiki_datastores section&lt;br /&gt;
# Deploy operations changes with Salt&lt;br /&gt;
## &#039;&#039;&#039;Nginx vhost.&#039;&#039;&#039; &amp;lt;code&amp;gt;salt-call --local state.sls roles/webserver-legacy/nginx&amp;lt;/code&amp;gt; or sometimes saas-mediawiki/nginx instead&lt;br /&gt;
## &#039;&#039;&#039;Datastores.&#039;&#039;&#039; &amp;lt;code&amp;gt;salt-call --local state.sls roles/saas-mediawiki/data&amp;lt;/code&amp;gt;&lt;br /&gt;
# Declare the dbname/host name pair for the new instance in the configuration&lt;br /&gt;
# Create a database&lt;br /&gt;
## In every case, &amp;lt;kbd&amp;gt;GRANT ALL PRIVILEGES on wikis.* TO &#039;mediawiki-saas&#039;@&#039;localhost&#039;;&amp;lt;/kbd&amp;gt;&lt;br /&gt;
## If you migrate an existing wiki from its db: &amp;lt;kbd&amp;gt;mysql yourwiki &amp;lt; dump.sql&amp;lt;/kbd&amp;gt; + &amp;lt;kbd&amp;gt;mw update yourwiki&amp;lt;/kbd&amp;gt; works well&lt;br /&gt;
## If it&#039;s a fresh new wiki: &amp;lt;kbd&amp;gt;mw addWiki yourwiki&amp;lt;/kbd&amp;gt; will create the database and populate the tables&lt;br /&gt;
&lt;br /&gt;
To deploy changes with Salt&lt;br /&gt;
&lt;br /&gt;
== How to deploy an extension or a skin? ==&lt;br /&gt;
&lt;br /&gt;
* Ensure it&#039;s available on the service&lt;br /&gt;
** If it&#039;s hosted on gerrit.wikimedia.org, add it to pillar/saas/mediawiki.sls file in the rOPS repository&lt;br /&gt;
** If not, you need to provide custom logic at roles/saas-mediawiki/mediawiki&lt;br /&gt;
* Configure it&lt;br /&gt;
** Edit the saas-mediawiki config/Settings.php folder, saasUseExtensionFoo will load the Foo extension and saasUseSkinBar will load the bar skin.&lt;br /&gt;
** If you need more advanced logic, config/CommonsSettings.php contain a good example for Scribunto&lt;br /&gt;
&lt;br /&gt;
== Where to configure settings? ==&lt;br /&gt;
=== MediaWiki wg variables ===&lt;br /&gt;
Most variables can directly be set in the saas-mediawiki repository under config/Settings.php.&lt;br /&gt;
&lt;br /&gt;
For any special logic, like transform values or set a bunch of values at once, can be added to CommonSettings.php. Feel free to create a task on DevCentral if you need assistance to do so.&lt;br /&gt;
&lt;br /&gt;
=== Interwiki ===&lt;br /&gt;
Interwiki configuration is peculiar, as it&#039;s a database.&lt;br /&gt;
&lt;br /&gt;
To update the interwiki:&lt;br /&gt;
# Update the configuration under mediawiki_interwikis at {{Ops file|pillar/saas/mediawiki.sls}}&lt;br /&gt;
# Deploy with &amp;lt;code&amp;gt;salt web-001 state.apply roles/saas-mediawiki/mediawiki/interwiki&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How to run a script against a wiki? ==&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;mw &amp;lt;script to run&amp;gt; &amp;lt;wiki name&amp;gt;&amp;lt;/code&amp;gt; command allow to run a maintenance script to a specific wiki.&lt;br /&gt;
&lt;br /&gt;
For example &amp;lt;kbd&amp;gt;sudo -u mediawiki mw update agora&amp;lt;/kbd&amp;gt; will run update.php for this wiki.&lt;br /&gt;
&lt;br /&gt;
== How to lock a wiki in read-only mode? ==&lt;br /&gt;
&lt;br /&gt;
Set saasReadOnly to Some(&amp;quot;Your explanation message&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
An example can be found at [https://devcentral.nasqueron.org/D3743 D3743].&lt;br /&gt;
&lt;br /&gt;
If the lock is for a maintenance, commit it so we&#039;ve a trace, then simply revert the change when the maintenance is done.&lt;br /&gt;
&lt;br /&gt;
== Upgrade ==&lt;br /&gt;
=== SaaS entry point ===&lt;br /&gt;
Should be automated with Jenkins CD.&lt;br /&gt;
&lt;br /&gt;
For now, as mediawiki user: &amp;lt;code&amp;gt;cd /srv/saas/mediawiki &amp;amp;&amp;amp; git pull &amp;amp;&amp;amp; composer update &amp;amp;&amp;amp; restart-php-fpm&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== MediaWiki ===&lt;br /&gt;
&lt;br /&gt;
As mediawiki user, assuming a clean branch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight language=bash&amp;gt;&lt;br /&gt;
cd /srv/mediawiki&lt;br /&gt;
git pull&lt;br /&gt;
&lt;br /&gt;
for dir in extensions/*/; do (cd &amp;quot;$dir&amp;quot; &amp;amp;&amp;amp; git pull); done&lt;br /&gt;
for dir in skins/*/; do (cd &amp;quot;$dir&amp;quot; &amp;amp;&amp;amp; git pull); done&lt;br /&gt;
&lt;br /&gt;
composer update&lt;br /&gt;
&lt;br /&gt;
mw update agora&lt;br /&gt;
mw update wolfplex&lt;br /&gt;
mw update wikis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Troubleshoot ===&lt;br /&gt;
==== Upgrade databases older than 1.39 ====&lt;br /&gt;
&lt;br /&gt;
Support has been removed for older versions upgrades.&lt;br /&gt;
&lt;br /&gt;
We need to create a temporary installation with that &amp;quot;installation relais&amp;quot; for a by-step update.&lt;br /&gt;
&lt;br /&gt;
There is no automation for that currently.&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Operations_repository&amp;diff=2582</id>
		<title>Operations grimoire/Operations repository</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Operations_repository&amp;diff=2582"/>
		<updated>2026-08-28T09:53:02Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* IDE configuration */ Only available in PyCharm Pro:&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Our configuration as code is stores in the operations (rOPS) repository.&lt;br /&gt;
&lt;br /&gt;
== Repository layout ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ Content of rOPS&lt;br /&gt;
|-&lt;br /&gt;
! Path !! Description&lt;br /&gt;
|-&lt;br /&gt;
| _modules || Custom execution modules&lt;br /&gt;
|-&lt;br /&gt;
| _states || Custom states modules&lt;br /&gt;
|-&lt;br /&gt;
| _tests || Unit tests for custom modules, scripts, tests for repo&lt;br /&gt;
|-&lt;br /&gt;
| hotfixes || Fixes you need to run to solve a problem on the servers&lt;br /&gt;
|-&lt;br /&gt;
| pillar || Configuration data structures to use in the states&lt;br /&gt;
|-&lt;br /&gt;
| roles || The states to deploy, divided in roles and then in units&lt;br /&gt;
|-&lt;br /&gt;
| utils || Helper scripts to maintain the repository&lt;br /&gt;
|-&lt;br /&gt;
| terraform || OpenTofu / Terraform infrastructure configuration&lt;br /&gt;
|-&lt;br /&gt;
| map.jinja || As we deploy on several OS and distros, mapping of packages names or directories&lt;br /&gt;
|-&lt;br /&gt;
| top.sls || topfile: what we deploy where?&lt;br /&gt;
|-&lt;br /&gt;
| PORTS || Documentation of the ports used in the configuration&lt;br /&gt;
|-&lt;br /&gt;
| UIDs || Documentation of the users used in the configuration&lt;br /&gt;
|-&lt;br /&gt;
| GIDs || Documentation of the groups used in the configuration&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Contributions howto ==&lt;br /&gt;
=== Repository source ===&lt;br /&gt;
You&#039;ll find the Operations repository at https://devcentral.nasqueron.org/source/operations/&lt;br /&gt;
&lt;br /&gt;
If DevCentral isn&#039;t available, a mirror of the repository can be found at https://github.com/nasqueron/operations.&lt;br /&gt;
&lt;br /&gt;
=== Workflow of contributions ===&lt;br /&gt;
We follow the general workflow is described at [[How to contribute code]].&lt;br /&gt;
&lt;br /&gt;
Before committing a change to the main branch, you can test a deployment on a server.&lt;br /&gt;
In that case, merge in main immediately after the deployment as the repository is the source of truth for the server state.&lt;br /&gt;
&lt;br /&gt;
Log what you do on #nasqueron-ops, so it will be included at https://infra.nasqueron.org/servers-log/&lt;br /&gt;
&lt;br /&gt;
=== Code conventions ===&lt;br /&gt;
&#039;&#039;See also: [[Code conventions]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Python code: run `black` before committing&lt;br /&gt;
* YAML: indent with two spaces, including list bullets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
id:&lt;br /&gt;
  method:&lt;br /&gt;
    - somekey: value&lt;br /&gt;
    - otherkey: value&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Environment setup ==&lt;br /&gt;
&lt;br /&gt;
Use of WindRiver is recommended if you wish to get an environment already installed for your contributions.&lt;br /&gt;
&lt;br /&gt;
=== Everywhere setup ===&lt;br /&gt;
This section cover topics needed both on our development servers or in your laptop.&lt;br /&gt;
&lt;br /&gt;
==== Python dependencies ====&lt;br /&gt;
&lt;br /&gt;
Create a virtual environment, then install dependencies from {{Ops file|requirements.txt}}:&lt;br /&gt;
&lt;br /&gt;
    mkdir -p $HOME/dev/python/ops&lt;br /&gt;
    python3 -m venv $HOME/dev/python/ops&lt;br /&gt;
    source $HOME/dev/python/ops/bin/activate&lt;br /&gt;
    pip install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
As of July 2026, it seems possible to also install Salt in the virtual environment and run tests:&lt;br /&gt;
&lt;br /&gt;
    pip install salt==3006.26&lt;br /&gt;
    make test&lt;br /&gt;
&lt;br /&gt;
(tested on WindRiver: with FreeBSD 15.1, Python 3.12.13, Salt 3006.26)&lt;br /&gt;
(tested on local workstation BlueDrake: with Fedora 42 Python 3.13.13, Salt 3006.26)&lt;br /&gt;
&lt;br /&gt;
==== Datacube ====&lt;br /&gt;
&lt;br /&gt;
If you&#039;re in the ops group, you&#039;ll need sometimes to deploy from branches. As our deployment server Complector doesn&#039;t have PHP installed (so no arc), you need to exchange branches through our datacube.&lt;br /&gt;
&lt;br /&gt;
On WindRiver:&lt;br /&gt;
&lt;br /&gt;
     git remote add datacube /datacube/git/operations.git&lt;br /&gt;
&lt;br /&gt;
Anywhere else:&lt;br /&gt;
&lt;br /&gt;
     git remote add datacube ssh://windriver.nasqueron.org/datacube/git/operations.git&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t in the ops groups, and have access to our devserver, you&#039;ll still be able to clone or pull from that directory, but not to write there (ie push branches).&lt;br /&gt;
&lt;br /&gt;
=== Local computer setup ===&lt;br /&gt;
&lt;br /&gt;
This section cover topics NOT needed on our development servers.&lt;br /&gt;
&lt;br /&gt;
==== macOS ====&lt;br /&gt;
&lt;br /&gt;
To run tests properly, install first:&lt;br /&gt;
&lt;br /&gt;
    brew install bats&lt;br /&gt;
    brew install python3.13&lt;br /&gt;
&lt;br /&gt;
You can now setup the virtual environment per everywhere setup instructions.&lt;br /&gt;
&lt;br /&gt;
Salt behaves correctly on macOS, so you can install it safely in the virtual environment:&lt;br /&gt;
&lt;br /&gt;
    pip install salt==3006.26&lt;br /&gt;
&lt;br /&gt;
Check the version on WindRiver or Complector with &amp;lt;code&amp;gt;salt --versions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, to run the tests, you need a recent version of make, but the BSD make currently shipped, as of Tahoe 26.5, isn&#039;t up-to-date:&lt;br /&gt;
&lt;br /&gt;
    brew install gmake&lt;br /&gt;
    (cd _tests &amp;amp;&amp;amp; gmake)&lt;br /&gt;
&lt;br /&gt;
==== Arcanist ====&lt;br /&gt;
&lt;br /&gt;
To use arc with the operations repository, our collection of patches is needed.&lt;br /&gt;
&lt;br /&gt;
If arcanist isn&#039;t installed through Git repository, best is to do like on devserver role:&lt;br /&gt;
&lt;br /&gt;
    mkdir /opt/phabricator &amp;amp;&amp;amp; cd /opt/phabricator&lt;br /&gt;
    git clone https://github.com/nasqueron/arcanist.git&lt;br /&gt;
    (cd arcanist &amp;amp;&amp;amp; git checkout production)&lt;br /&gt;
    ln -s /opt/phabricator/arcanist/bin/arc /usr/local/bin/&lt;br /&gt;
    git clone https://devcentral.nasqueron.org/source/shellcheck-linter.git&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve already cloned Arcanist repository, switch to our branch production:&lt;br /&gt;
&lt;br /&gt;
    git remote add nasqueron git@github.com:nasqueron/arcanist.git&lt;br /&gt;
    git fetch --all&lt;br /&gt;
    git checkout production&lt;br /&gt;
&lt;br /&gt;
As of August 2024, there is no plan to make that repository available on DevCentral.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll also need shellcheck library-linter library, normally at the same level than your arcanist repository.&lt;br /&gt;
&lt;br /&gt;
==== IDE configuration ====&lt;br /&gt;
[[File:PyCharm-TemplateLanguages.png|thumb|right|Screenshot of correctly configured template languages]]&lt;br /&gt;
The states .sls file are Jinja2 templates producing YAML files, or &amp;quot;jinja2+yaml&amp;quot;. If this case can&#039;t be handled by your IDE, it works best to use YAML syntax highlighting.&lt;br /&gt;
&lt;br /&gt;
If you use PyCharm Pro or IntelliJ IDEA Ultimate, you can configure this scheme:&lt;br /&gt;
&lt;br /&gt;
* Allow YAML files to be Jinja2 templates&lt;br /&gt;
** Go to Settings &amp;gt; Languages &amp;amp; Frameworks &amp;gt; Template Languages&lt;br /&gt;
** Select Jinja2 as template language&lt;br /&gt;
** Add YAML in the list&lt;br /&gt;
* Open SLS as YAML&lt;br /&gt;
&lt;br /&gt;
== Linting ==&lt;br /&gt;
&lt;br /&gt;
=== pre-commit ===&lt;br /&gt;
You can use &#039;&#039;&#039;pre-commit&#039;&#039;&#039; you can install with &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; to get a hook running Python code to lint the repository before a commit.&lt;br /&gt;
&lt;br /&gt;
Beware if you use &amp;lt;code&amp;gt;arc diff&amp;lt;/code&amp;gt; without a commit first: your message will be lost if pre-commit raises an error, always use &amp;lt;code&amp;gt;git commit&amp;lt;/code&amp;gt; first.&lt;br /&gt;
&lt;br /&gt;
On FreeBSD, there is an issue to build with clang 16+ the ruamel yaml clib library. You can build the FreeBSD port devel/py-ruamel.yaml.clib and copy the .whl to a stable path, then install it in your virtual environment.&lt;br /&gt;
&lt;br /&gt;
On WindRiver, the .whl is available in /opt/python/py311, so you can:&lt;br /&gt;
&lt;br /&gt;
    $ python3 -m venv /path/to/your/virtualenv&lt;br /&gt;
    $ source /path/to/your/virtualenv/bin/activate&lt;br /&gt;
    $ pip install /opt/python/py311/ruamel.yaml.clib-0.2.8-cp311-cp311-freebsd_14_0_release_p3_amd64.whl&lt;br /&gt;
    $ pip install pre-commit&lt;br /&gt;
    $ make&lt;br /&gt;
    pre-commit install&lt;br /&gt;
    pre-commit installed at .git/hooks/pre-commit&lt;br /&gt;
    $ rehash&lt;br /&gt;
    $ pre-commit&lt;br /&gt;
&lt;br /&gt;
== Refactoring ==&lt;br /&gt;
&lt;br /&gt;
=== Rename a lot of files ===&lt;br /&gt;
&lt;br /&gt;
To move a directory and rename the &#039;&#039;Source file&#039;&#039; headers:&lt;br /&gt;
&lt;br /&gt;
    git mv roles/webserver-legacy/nginx/files roles/webserver-alkane/nginx/&lt;br /&gt;
    cd roles/webserver-alkane/nginx/&lt;br /&gt;
    find . -type f -name &#039;*.conf&#039; | xargs gsed -i s/webserver-legacy/webserver-alkane/g&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[/FAQ]] - Frequently asked questions about the Operations repository&lt;br /&gt;
* [[How to contribute code]]&lt;br /&gt;
* [[Devserver reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Template:User&amp;diff=2581</id>
		<title>Template:User</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Template:User&amp;diff=2581"/>
		<updated>2026-08-23T11:42:52Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: {{{DevCentral user board}}} is now optional&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:&amp;lt;span style=&amp;quot;color:white;width:0;font-size:0;&amp;quot;&amp;gt;{{FULLPAGENAME}}&amp;lt;/span&amp;gt;}}&lt;br /&gt;
&amp;lt;div class=&amp;quot;block user-block&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size: 3.75em; margin-bottom: 0.5em;&amp;quot; class=&amp;quot;color-magnetic-five&amp;quot;&amp;gt;{{PAGENAME}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size: 3em; margin-bottom: 10vh;&amp;quot; class=&amp;quot;color-magnetic-five&amp;quot;&amp;gt;{{{Full name|}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;block-col block-col-half&amp;quot;&amp;gt;&lt;br /&gt;
{{{Presentation}}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;block-col block-col-quarter&amp;quot;&amp;gt;&lt;br /&gt;
;Projects&lt;br /&gt;
:{{{Projects}}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;block-col block-col-quarter&amp;quot;&amp;gt;&lt;br /&gt;
;DevCentral&lt;br /&gt;
{{#if:{{{DevCentral user board|}}}|:▥ [https://devcentral.nasqueron.org/tag/{{{DevCentral user board}}} Personal user board]}}&lt;br /&gt;
:▤ [https://devcentral.nasqueron.org/p/{{{DevCentral user feed}}}/ View my feed]&lt;br /&gt;
;External sites&lt;br /&gt;
:⚓ [https://github.com/{{{GitHub}}} GitHub]&lt;br /&gt;
:⚓ {{{Personal site|}}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Ptdradmin&amp;diff=2580</id>
		<title>User:Ptdradmin</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Ptdradmin&amp;diff=2580"/>
		<updated>2026-08-23T11:40:31Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Fixing link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{User&lt;br /&gt;
| Full name = Gui Martinien Doba&lt;br /&gt;
| Presentation =&lt;br /&gt;
;Contributions&lt;br /&gt;
* Nasqueron Operations SIG apprentice&lt;br /&gt;
* Currently working on Stud&#039;In&lt;br /&gt;
&lt;br /&gt;
;Field notes&lt;br /&gt;
* See [[/Notes]] (mostly in French)&lt;br /&gt;
&lt;br /&gt;
| Projects = [[Operations grimoire|Ops]]&lt;br /&gt;
| DevCentral user feed = ptdradmin&lt;br /&gt;
| GitHub = ptdradmin&lt;br /&gt;
| Personal site = [https://www.stud-in.be/ stud-in.be]&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Ptdradmin&amp;diff=2579</id>
		<title>User:Ptdradmin</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Ptdradmin&amp;diff=2579"/>
		<updated>2026-08-23T11:40:17Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{User&lt;br /&gt;
| Full name = Gui Martinien Doba&lt;br /&gt;
| Presentation =&lt;br /&gt;
;Contributions&lt;br /&gt;
* Nasqueron Operations SIG apprentice&lt;br /&gt;
* Currently working on Stud&#039;In&lt;br /&gt;
&lt;br /&gt;
;Field notes&lt;br /&gt;
* See [[/notes]] (mostly in French)&lt;br /&gt;
&lt;br /&gt;
| Projects = [[Operations grimoire|Ops]]&lt;br /&gt;
| DevCentral user feed = ptdradmin&lt;br /&gt;
| GitHub = ptdradmin&lt;br /&gt;
| Personal site = [https://www.stud-in.be/ stud-in.be]&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Ptdradmin/Notes&amp;diff=2578</id>
		<title>User:Ptdradmin/Notes</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Ptdradmin/Notes&amp;diff=2578"/>
		<updated>2026-08-23T11:38:36Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Created page with &amp;quot;== Field notes ==  {{Special:Prefixindex/User:Ptdradmin/Notes/|stripprefix=1}}  ;See also * Dev zone/Create devserver access/fr  Category:Notes&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Field notes == &lt;br /&gt;
{{Special:Prefixindex/User:Ptdradmin/Notes/|stripprefix=1}}&lt;br /&gt;
&lt;br /&gt;
;See also&lt;br /&gt;
* [[Dev zone/Create devserver access/fr]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Notes]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Ptdradmin/Notes/Anubis/fr&amp;diff=2577</id>
		<title>User:Ptdradmin/Notes/Anubis/fr</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Ptdradmin/Notes/Anubis/fr&amp;diff=2577"/>
		<updated>2026-08-23T11:31:49Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Dereckson moved page Fr to User:Ptdradmin/Notes/Anubis/fr without leaving a redirect: Lost &amp;amp; found - Field notes on Anubis &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Documentation - Résolution d&#039;erreur de build Anubis =&lt;br /&gt;
&lt;br /&gt;
== Contexte ==&lt;br /&gt;
Installation et configuration d’&#039;&#039;&#039;Anubis Web AI Firewall&#039;&#039;&#039; sur un serveur &#039;&#039;&#039;Red Hat Enterprise Linux 10 (RHEL 10)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Serveur :&#039;&#039;&#039; dwellers.nasqueron.org&lt;br /&gt;
* &#039;&#039;&#039;Utilisateur :&#039;&#039;&#039; ptdradmin&lt;br /&gt;
* &#039;&#039;&#039;Répertoire d’installation :&#039;&#039;&#039; /opt/anubis&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Versions des outils :&#039;&#039;&#039;&lt;br /&gt;
* Go : 1.25.3&lt;br /&gt;
* Node.js : 22.16.0&lt;br /&gt;
* npm : 10.9.2&lt;br /&gt;
* esbuild : 0.27.2&lt;br /&gt;
&lt;br /&gt;
== Problème rencontré ==&lt;br /&gt;
=== Symptômes ===&lt;br /&gt;
# Échec de la génération de code (&amp;lt;code&amp;gt;go generate ./...&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Permissions refusées lors de la création de fichiers&lt;br /&gt;
* Messages : &amp;lt;code&amp;gt;permission denied&amp;lt;/code&amp;gt;&lt;br /&gt;
* Erreur : &amp;lt;code&amp;gt;./build.sh: line 48: esbuild: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Échec de la compilation (&amp;lt;code&amp;gt;go build&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Le binaire &amp;lt;code&amp;gt;/opt/anubis/var/anubis&amp;lt;/code&amp;gt; n&#039;a pas été créé&lt;br /&gt;
* Erreur : &amp;lt;code&amp;gt;pattern static/app.js: no matching files found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Échec du démarrage du service systemd&lt;br /&gt;
* Service anubis ne peut pas démarrer&lt;br /&gt;
* Erreur : &amp;lt;code&amp;gt;Unable to locate executable &#039;/usr/bin/anubis&#039;: No such file or directory&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Erreurs détaillées ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
go generate ./...&lt;br /&gt;
&lt;br /&gt;
stringer: writing output: open droneblresponse_string.go: permission denied&lt;br /&gt;
(✗) Error [ error=failed to generate code for &amp;quot;/opt/anubis/internal/honeypot/naive/page.templ&amp;quot;: &lt;br /&gt;
    failed to write target file &amp;quot;/opt/anubis/internal/honeypot/naive/page_templ.go&amp;quot;: &lt;br /&gt;
    open /opt/anubis/internal/honeypot/naive/page_templ.go: permission denied ]&lt;br /&gt;
mkdir: cannot create directory &#039;static/js&#039;: Permission denied&lt;br /&gt;
./build.sh: line 48: esbuild: command not found&lt;br /&gt;
lib/challenge/preact/preact.go:20: running &amp;quot;./build.sh&amp;quot;: exit status 127&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
go build -o var/anubis cmd/anubis/main.go&lt;br /&gt;
&lt;br /&gt;
lib/challenge/preact/preact.go:23:12: pattern static/app.js: no matching files found&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl status anubis&lt;br /&gt;
&lt;br /&gt;
× anubis.service - Anubis Web AI Firewall&lt;br /&gt;
     Active: failed (Result: exit-code)&lt;br /&gt;
    Process: 821872 ExecStart=/usr/bin/anubis (code=exited, status=203/EXEC)&lt;br /&gt;
&lt;br /&gt;
anubis.service: Unable to locate executable &#039;/usr/bin/anubis&#039;: No such file or directory&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Analyse de la cause racine ==&lt;br /&gt;
=== Causes principales ===&lt;br /&gt;
* Conflit de permissions dû à l&#039;utilisation de &amp;lt;code&amp;gt;sudo git clone&amp;lt;/code&amp;gt;&lt;br /&gt;
* Outil &#039;&#039;esbuild&#039;&#039; manquant dans le PATH lors de l’exécution de &amp;lt;code&amp;gt;build.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Conséquences en cascade ===&lt;br /&gt;
* Génération incomplète : fichiers de code généré non créés&lt;br /&gt;
* esbuild non trouvé : bundler JavaScript inaccessible&lt;br /&gt;
* Assets manquants : fichiers JS comme &amp;lt;code&amp;gt;static/app.js&amp;lt;/code&amp;gt; non générés&lt;br /&gt;
* Compilation impossible&lt;br /&gt;
* Binaire absent : /usr/bin/anubis inexistant&lt;br /&gt;
* Service systemd en échec&lt;br /&gt;
&lt;br /&gt;
== Solution ==&lt;br /&gt;
&amp;lt;folding summary=&amp;quot;Cliquez pour dérouler les étapes complètes de résolution&amp;quot;&amp;gt;&lt;br /&gt;
=== Étape 1 : Arrêter le service en échec ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl stop anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 2 : Installer esbuild globalement ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo npm install -g esbuild&lt;br /&gt;
which esbuild&lt;br /&gt;
esbuild --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 3 : Corriger la propriété des fichiers ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo chown -R ptdradmin:ptdradmin /opt/anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 4 : Nettoyer les artéfacts de build partiels ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /opt/anubis&lt;br /&gt;
rm -rf bin var&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 5 : Générer le code sans sudo ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
go generate ./...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* La génération doit se terminer avec succès et produire les fichiers JS :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(✓) Complete [ updates=0 duration=2.753344ms ]&lt;br /&gt;
(✓) Complete [ updates=0 duration=4.775691ms ]&lt;br /&gt;
app.js&lt;br /&gt;
  static/app.js  20.3kb&lt;br /&gt;
⚡ Done in 12ms&lt;br /&gt;
(✓) Complete [ updates=0 duration=2.333324ms ]&lt;br /&gt;
(✓) Complete [ updates=0 duration=3.490825ms ]&lt;br /&gt;
(✓) Complete [ updates=0 duration=14.668559ms ]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 6 : Compiler le binaire ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
go build -o var/anubis cmd/anubis/main.go&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 7 : Vérifier la création du binaire ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls -l var/anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Exemple attendu :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
-rwxr-xr-x. 1 ptdradmin ptdradmin 52659096 Dec 17 14:46 var/anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 8 : Copier le binaire vers /usr/bin ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo cp var/anubis /usr/bin/anubis&lt;br /&gt;
sudo chmod 755 /usr/bin/anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 9 : Vérifier l&#039;installation ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/usr/bin/anubis --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Exemple attendu : &amp;lt;code&amp;gt;Anubis devel&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 10 : Démarrer le service ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl start anubis&lt;br /&gt;
sudo systemctl status anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Statut attendu :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
● anubis.service - Anubis Web AI Firewall&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/anubis.service; enabled; preset: disabled)&lt;br /&gt;
     Active: active (running) since Wed 2025-12-17 14:46:41 UTC; 6s ago&lt;br /&gt;
   Main PID: 823482 (anubis)&lt;br /&gt;
      Tasks: 13 (limit: 203296)&lt;br /&gt;
     Memory: 15.5M (peak: 18.7M)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Étape 11 : Commandes complètes de séquence ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Arrêter le service&lt;br /&gt;
sudo systemctl stop anubis&lt;br /&gt;
&lt;br /&gt;
# S&#039;assurer que esbuild est installé&lt;br /&gt;
sudo npm install -g esbuild&lt;br /&gt;
which esbuild&lt;br /&gt;
esbuild --version&lt;br /&gt;
&lt;br /&gt;
# Corriger la propriété&lt;br /&gt;
sudo chown -R ptdradmin:ptdradmin /opt/anubis&lt;br /&gt;
&lt;br /&gt;
# Nettoyer et reconstruire&lt;br /&gt;
cd /opt/anubis&lt;br /&gt;
rm -rf bin var&lt;br /&gt;
go generate ./...&lt;br /&gt;
go build -o var/anubis cmd/anubis/main.go&lt;br /&gt;
&lt;br /&gt;
# Vérifier et installer&lt;br /&gt;
ls -l var/anubis&lt;br /&gt;
sudo cp var/anubis /usr/bin/anubis&lt;br /&gt;
sudo chmod 755 /usr/bin/anubis&lt;br /&gt;
&lt;br /&gt;
# Tester et démarrer&lt;br /&gt;
/usr/bin/anubis --version&lt;br /&gt;
sudo systemctl start anubis&lt;br /&gt;
sudo systemctl status anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/folding&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bonnes pratiques ==&lt;br /&gt;
* Ne pas utiliser &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; pour &amp;lt;code&amp;gt;git clone&amp;lt;/code&amp;gt;&lt;br /&gt;
* Installer tous les outils nécessaires globalement avant le build&lt;br /&gt;
* Séparer les opérations utilisateur et root (build vs installation)&lt;br /&gt;
* Vérifier les permissions avant de lancer le build&lt;br /&gt;
* Utiliser &amp;lt;code&amp;gt;make build&amp;lt;/code&amp;gt; si disponible&lt;br /&gt;
&lt;br /&gt;
== Structure de répertoires recommandée ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
/opt/anubis/          # Code source (propriétaire: utilisateur)&lt;br /&gt;
  ├── cmd/&lt;br /&gt;
  ├── internal/&lt;br /&gt;
  ├── lib/&lt;br /&gt;
  └── var/&lt;br /&gt;
      └── anubis      # Binaire compilé&lt;br /&gt;
&lt;br /&gt;
/usr/bin/anubis       # Installation système (root)&lt;br /&gt;
/etc/anubis/          # Configuration (root)&lt;br /&gt;
  ├── site.env&lt;br /&gt;
  └── site.botPolicies.yaml&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vérifications post-installation ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo systemctl status anubis&lt;br /&gt;
sudo journalctl -u anubis -n 50&lt;br /&gt;
sudo journalctl -u anubis -f&lt;br /&gt;
sudo ss -tulpn | grep anubis&lt;br /&gt;
sudo systemctl restart anubis&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Résumé ==&lt;br /&gt;
* Problèmes : permissions incorrectes et esbuild manquant&lt;br /&gt;
* Solutions : installer esbuild globalement, corriger propriété des fichiers, reconstruire sans sudo&lt;br /&gt;
* Résultat final : le service Anubis fonctionne correctement et protège le site desk.nasqueron.org&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Ptdradmin&amp;diff=2576</id>
		<title>User:Ptdradmin</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Ptdradmin&amp;diff=2576"/>
		<updated>2026-08-23T11:28:42Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Created page with &amp;quot;{{User | Full name = Gui Martinien Doba | Presentation = * Nasqueron Operations SIG apprentice * Currently working on Stud&amp;#039;In | Projects = Ops | DevCentral user feed = ptdradmin | GitHub = ptdradmin | Personal site = [https://www.stud-in.be/ stud-in.be] }}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{User&lt;br /&gt;
| Full name = Gui Martinien Doba&lt;br /&gt;
| Presentation = * Nasqueron Operations SIG apprentice&lt;br /&gt;
* Currently working on Stud&#039;In&lt;br /&gt;
| Projects = [[Operations grimoire|Ops]]&lt;br /&gt;
| DevCentral user feed = ptdradmin&lt;br /&gt;
| GitHub = ptdradmin&lt;br /&gt;
| Personal site = [https://www.stud-in.be/ stud-in.be]&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2575</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2575"/>
		<updated>2026-08-23T11:21:34Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Switch to full t-rex&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex-complete.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
	background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Devserver/Git&amp;diff=2574</id>
		<title>User:Dereckson/Devserver/Git</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Devserver/Git&amp;diff=2574"/>
		<updated>2026-08-23T04:29:07Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Documentation by GPT-5.6-Sol for git bye / newbug and the new receive / resend / land new version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
A small set of custom Git commands for working with short-lived feature branches is added to $HOME/bin.&lt;br /&gt;
&lt;br /&gt;
The commands implement a change-centric workflow inspired by Phabricator&#039;s &amp;lt;code&amp;gt;arc land&amp;lt;/code&amp;gt; model and Gerrit:&lt;br /&gt;
&lt;br /&gt;
* the default branch is the canonical project history;&lt;br /&gt;
* one feature branch represents one logical change;&lt;br /&gt;
* the feature commit may be amended while the change is under development;&lt;br /&gt;
* feature branches may be published to a remote for review, testing or deployment;&lt;br /&gt;
* once accepted, the change is landed on the default branch;&lt;br /&gt;
* feature branches are disposable and are removed after landing.&lt;br /&gt;
&lt;br /&gt;
The normal lifecycle is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
newbug → edit/commit → resend ↔ receive → land&lt;br /&gt;
                                     ↘&lt;br /&gt;
                                      bye&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;land&amp;lt;/code&amp;gt; is used when landing the change locally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;bye&amp;lt;/code&amp;gt; is used when the change has already been merged elsewhere, for example through a forge or code-review interface.&lt;br /&gt;
&lt;br /&gt;
== Command reference ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Command&lt;br /&gt;
! Purpose&lt;br /&gt;
! Typical use&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git get-default-branch&amp;lt;/code&amp;gt;&lt;br /&gt;
| Determine the repository&#039;s integration branch&lt;br /&gt;
| Used directly and by the other workflow commands&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git newbug BRANCH&amp;lt;/code&amp;gt;&lt;br /&gt;
| Start a new change from an updated default branch&lt;br /&gt;
| Beginning a task&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git resend&amp;lt;/code&amp;gt;&lt;br /&gt;
| Amend and republish the current feature commit&lt;br /&gt;
| Send a new revision for testing or review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git receive&amp;lt;/code&amp;gt;&lt;br /&gt;
| Replace the local feature checkout with its published version&lt;br /&gt;
| Development or deployment machine&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git land&amp;lt;/code&amp;gt;&lt;br /&gt;
| Integrate the feature commit and clean up the branch&lt;br /&gt;
| Completing a locally landed change&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git bye&amp;lt;/code&amp;gt;&lt;br /&gt;
| Return to the default branch and remove the feature branch&lt;br /&gt;
| Change already merged elsewhere&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git delete-remote-branch&amp;lt;/code&amp;gt;&lt;br /&gt;
| Delete a branch from a remote repository&lt;br /&gt;
| Explicit remote branch cleanup&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Philosophy ==&lt;br /&gt;
&lt;br /&gt;
These commands deliberately use Git as a change-oriented version-control system.&lt;br /&gt;
&lt;br /&gt;
The permanent artifact is the clean history of accepted changes on the default branch.&lt;br /&gt;
&lt;br /&gt;
A feature branch is temporary infrastructure around one of those changes. While the change is being prepared, its commit can be amended and republished as many times as necessary.&lt;br /&gt;
&lt;br /&gt;
Consequently:&lt;br /&gt;
&lt;br /&gt;
* rewriting a private feature branch is normal;&lt;br /&gt;
* rewriting the default branch is not;&lt;br /&gt;
* feature branches are short-lived;&lt;br /&gt;
* intermediate correction commits do not need to survive forever;&lt;br /&gt;
* landing turns the final version of the change into permanent project history;&lt;br /&gt;
* local and remote feature branches can then be discarded.&lt;br /&gt;
&lt;br /&gt;
This is closer to the change model used by Phabricator or Gerrit than to workflows where the complete development history of every feature branch is retained after merging.&lt;br /&gt;
&lt;br /&gt;
== Git subcommands ==&lt;br /&gt;
&lt;br /&gt;
The commands are installed as executables named &amp;lt;code&amp;gt;git-NAME&amp;lt;/code&amp;gt; in the user&#039;s &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Git automatically exposes such executables as subcommands.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git get-default-branch&lt;br /&gt;
main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
invokes the &amp;lt;code&amp;gt;git-get-default-branch&amp;lt;/code&amp;gt; executable.&lt;br /&gt;
&lt;br /&gt;
== Typical workflow ==&lt;br /&gt;
&lt;br /&gt;
=== Start a change ===&lt;br /&gt;
&lt;br /&gt;
Start from the repository&#039;s default branch and create a new feature branch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git newbug fix-vault-policy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;git newbug&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# determines the repository&#039;s default branch;&lt;br /&gt;
# switches to it;&lt;br /&gt;
# updates it from the remote repository;&lt;br /&gt;
# creates the requested feature branch.&lt;br /&gt;
&lt;br /&gt;
The resulting history is conceptually:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
A---B---C  main&lt;br /&gt;
         \&lt;br /&gt;
          D  fix-vault-policy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The feature branch is intended to contain one logical change.&lt;br /&gt;
&lt;br /&gt;
After editing files, create the initial commit normally:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git add -p&lt;br /&gt;
$ git commit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git commit -m &amp;quot;vault: restrict access to production secrets&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Amend and republish a change ===&lt;br /&gt;
&lt;br /&gt;
During development, the commit may be updated instead of accumulating fixup commits.&lt;br /&gt;
&lt;br /&gt;
Use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git resend&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command interactively stages changes with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
git add -p&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and amends the current commit while retaining its commit message.&lt;br /&gt;
&lt;br /&gt;
The updated feature branch is then published.&lt;br /&gt;
&lt;br /&gt;
If a remote named &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt; exists, it is preferred:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
workstation&lt;br /&gt;
    |&lt;br /&gt;
    | git resend&lt;br /&gt;
    v&lt;br /&gt;
datacube/fix-vault-policy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Otherwise, the branch is pushed to &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Because amending a commit changes its object ID, publishing a new revision requires replacing the previously published feature-branch commit.&lt;br /&gt;
&lt;br /&gt;
This rewriting is expected for short-lived feature branches. It must not be used to rewrite shared permanent branches such as &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== DevCentral repositories ====&lt;br /&gt;
&lt;br /&gt;
For repositories hosted on DevCentral, &amp;lt;code&amp;gt;git resend&amp;lt;/code&amp;gt; does not automatically push to &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Use Arcanist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ arc diff&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or configure a &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt; remote for the workstation-to-server sharing workflow.&lt;br /&gt;
&lt;br /&gt;
=== Receive a published revision on another machine ===&lt;br /&gt;
&lt;br /&gt;
A development or deployment machine can synchronize its current feature branch with the version published from the workstation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git receive&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command fetches the remote repositories and then resets the current feature branch to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
datacube/&amp;lt;current-branch&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
when &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt; exists, or otherwise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
origin/&amp;lt;current-branch&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, after publishing a Salt change from the workstation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
workstation$ git resend&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the development server can receive and test it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
devserver$ git receive&lt;br /&gt;
devserver$ salt-call saltutil.sync_modules&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warning:&#039;&#039;&#039; &amp;lt;code&amp;gt;git receive&amp;lt;/code&amp;gt; uses &amp;lt;code&amp;gt;git reset --hard&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Tracked changes in the index and working tree which are not present in the selected remote branch are discarded.&lt;br /&gt;
&lt;br /&gt;
Untracked files are not removed.&lt;br /&gt;
&lt;br /&gt;
The command is therefore intended for feature branches on development or deployment checkouts where the remote version is authoritative.&lt;br /&gt;
&lt;br /&gt;
== Landing a change ==&lt;br /&gt;
&lt;br /&gt;
When the feature commit is ready to become part of the repository&#039;s canonical history, run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git land&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command:&lt;br /&gt;
&lt;br /&gt;
# fetches remote changes;&lt;br /&gt;
# switches to the default branch;&lt;br /&gt;
# updates the default branch;&lt;br /&gt;
# cherry-picks the feature commit;&lt;br /&gt;
# pushes the updated default branch;&lt;br /&gt;
# deletes the local feature branch;&lt;br /&gt;
# deletes the published feature branch from &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt;, or from &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt; when no &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt; remote exists.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Before:&lt;br /&gt;
&lt;br /&gt;
A---B---C---E  main&lt;br /&gt;
         \&lt;br /&gt;
          D&#039;  fix-vault-policy&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
git land&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After:&lt;br /&gt;
&lt;br /&gt;
A---B---C---E---D&#039;&#039;  main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The feature branch is then removed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;D&#039;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;D&#039;&#039;&amp;lt;/code&amp;gt; represent the same logical change at different points of its Git history: cherry-picking creates a new commit on the default branch.&lt;br /&gt;
&lt;br /&gt;
=== One change per branch ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;git land&amp;lt;/code&amp;gt; lands the commit at the tip of the current feature branch.&lt;br /&gt;
&lt;br /&gt;
The workflow therefore expects a feature branch to represent one logical commit.&lt;br /&gt;
&lt;br /&gt;
During review or testing, update that commit with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git resend&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
rather than adding a succession of commits such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Implement feature&lt;br /&gt;
Fix typo&lt;br /&gt;
Actually fix test&lt;br /&gt;
Address review&lt;br /&gt;
Fix lint&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Those intermediate states are useful while developing the change, but do not need to become part of the permanent project history.&lt;br /&gt;
&lt;br /&gt;
== Cleaning up an externally merged branch ==&lt;br /&gt;
&lt;br /&gt;
Sometimes the change is landed by another system, for example after merging a pull request or accepting a code review.&lt;br /&gt;
&lt;br /&gt;
In that case, there is nothing left for &amp;lt;code&amp;gt;git land&amp;lt;/code&amp;gt; to integrate.&lt;br /&gt;
&lt;br /&gt;
From the feature branch, run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git bye&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command switches back to the repository&#039;s default branch, updates it when appropriate, and deletes the previous local feature branch.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git status --short --branch&lt;br /&gt;
## fix-vault-policy&lt;br /&gt;
&lt;br /&gt;
$ git bye&lt;br /&gt;
&lt;br /&gt;
$ git status --short --branch&lt;br /&gt;
## main...origin/main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;git bye&amp;lt;/code&amp;gt; force-deletes the local feature branch.&lt;br /&gt;
&lt;br /&gt;
Use it only when the change has already been integrated elsewhere or when the local branch is deliberately no longer needed.&lt;br /&gt;
&lt;br /&gt;
== Determining the default branch ==&lt;br /&gt;
&lt;br /&gt;
Use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git get-default-branch&lt;br /&gt;
main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command determines the branch which should be treated as the repository&#039;s integration branch.&lt;br /&gt;
&lt;br /&gt;
Examples include:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
master&lt;br /&gt;
production&lt;br /&gt;
dev&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other workflow commands use this helper instead of assuming that every repository uses &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git get-default-branch&lt;br /&gt;
production&lt;br /&gt;
&lt;br /&gt;
$ git newbug update-webserver&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create &amp;lt;code&amp;gt;update-webserver&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;production&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Deleting a remote branch ==&lt;br /&gt;
&lt;br /&gt;
A remote branch can be explicitly removed with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git delete-remote-branch origin old-feature&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which is equivalent to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git push origin --delete old-feature&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The current implementation also supports using only the branch name:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git delete-remote-branch old-feature&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In that form, &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt; is used as the remote.&lt;br /&gt;
&lt;br /&gt;
The historical default with no arguments is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;console&amp;quot;&amp;gt;&lt;br /&gt;
$ git delete-remote-branch&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which deletes &amp;lt;code&amp;gt;origin/master&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warning:&#039;&#039;&#039; the zero-argument form is intended for historical &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;-to-&amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; migration work. An explicit branch name should normally be provided.&lt;br /&gt;
&lt;br /&gt;
== Remote selection ==&lt;br /&gt;
&lt;br /&gt;
Some commands recognize a remote named &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This remote is intended as an intermediate location for sharing work-in-progress branches between a workstation and development or deployment machines.&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt; exists:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
workstation&lt;br /&gt;
    |&lt;br /&gt;
    | git resend&lt;br /&gt;
    v&lt;br /&gt;
datacube&lt;br /&gt;
    |&lt;br /&gt;
    | git receive&lt;br /&gt;
    v&lt;br /&gt;
development server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Without &amp;lt;code&amp;gt;datacube&amp;lt;/code&amp;gt;, the equivalent workflow uses &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt; where appropriate.&lt;br /&gt;
&lt;br /&gt;
This separates two concepts:&lt;br /&gt;
&lt;br /&gt;
* the repository hosting service used for permanent collaboration;&lt;br /&gt;
* an optional branch transport used to send a work-in-progress change to another machine.&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Devserver&amp;diff=2573</id>
		<title>User:Dereckson/Devserver</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Devserver&amp;diff=2573"/>
		<updated>2026-08-23T04:25:01Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Git ==&lt;br /&gt;
[[/Git]]&lt;br /&gt;
&lt;br /&gt;
== MediaWiki ==&lt;br /&gt;
=== Logs ===&lt;br /&gt;
tail -n 100 -f /var/log/www/dereckson.be/mediawiki-php.log&lt;br /&gt;
&lt;br /&gt;
=== Reinstall ===&lt;br /&gt;
* /var/51-wwwroot/mediawiki-dereckson/core: wikimedia/core repository&lt;br /&gt;
* /var/51-wwwroot/mediawiki-dereckson/core/extensions: symlink to &amp;lt;code&amp;gt;../extensions&amp;lt;/code&amp;gt;&lt;br /&gt;
** Clone all needed extensions in /var/51-wwwroot/mediawiki-dereckson/extensions&lt;br /&gt;
* /var/51-wwwroot/mediawiki-dereckson/core/skins: symlink to &amp;lt;code&amp;gt;../skins&amp;lt;/code&amp;gt;&lt;br /&gt;
** Same with git clone ssh://review/mediawiki/skins/&lt;br /&gt;
* /var/dataroot/mediawiki.dereckson.be/data contains:&lt;br /&gt;
** my_wiki.sqlite for the content&lt;br /&gt;
** wikicache.sqlite for the cache&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
* &#039;&#039;&#039;Core:&#039;&#039;&#039; mw-update-core (currently writing it)&lt;br /&gt;
&lt;br /&gt;
=== Create a new extension ===&lt;br /&gt;
;I. Wikimedia side&lt;br /&gt;
* [[mw:Manual:Developing_extensions]] &lt;br /&gt;
* [[mw:Gerrit/New repositories/Requests]]&lt;br /&gt;
* [https://phabricator.wikimedia.org/maniphest/task/edit/form/1/?projects=Project-Admins Create task for new Phabricator project]&lt;br /&gt;
&lt;br /&gt;
;II. WindRiver&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ setenv MW_EXTENSION foo&lt;br /&gt;
$ cd ~/dev/wikimedia/mediawiki/extensions&lt;br /&gt;
$ git clone ssh://review/mediawiki/extensions/$MW_EXTENSION || git init $MW_EXTENSION &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extension is actually stored at /var/51-wwwroot/mediawiki-dereckson/extensions, so can immediately be configured to test locally.&lt;br /&gt;
&lt;br /&gt;
;III. SaaS deployment&lt;br /&gt;
* {{Ops file|pillar/saas/mediawiki.sls}}&lt;br /&gt;
* [https://devcentral.nasqueron.org/source/saas-mediawiki/browse/main/config/Settings.php config/Settings.php]&lt;br /&gt;
&lt;br /&gt;
=== Troubleshoot ===&lt;br /&gt;
==== SQLite database is read-only ====&lt;br /&gt;
Runtime error near line 2: attempt to write a readonly database (8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
chown -R web-be-dereckson-mw:dereckson /var/dataroot/mediawiki.dereckson.be/data&lt;br /&gt;
chmod 771 /var/dataroot/mediawiki.dereckson.be/data&lt;br /&gt;
chmod 660 /var/dataroot/mediawiki.dereckson.be/data/my_wiki.sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Permissions to run tests and maintenance scripts ===&lt;br /&gt;
To run tests against wiki and maintenance scripts as user can be tricky:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
touch /tmp/mw-GlobalIdGenerator5001-UID-nodeid /tmp/mw-GlobalIdGenerator5001-UUID-128&lt;br /&gt;
chown web-be-dereckson-mw:dereckson /tmp/mw-GlobalIdGenerator5001-*&lt;br /&gt;
chmod 664 /tmp/mw-GlobalIdGenerator5001-*&lt;br /&gt;
&lt;br /&gt;
chmod 771 /var/dataroot/mediawiki.dereckson.be/data/locks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Zed ==&lt;br /&gt;
=== Useful paths ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Paths for Zed development&lt;br /&gt;
|-&lt;br /&gt;
! File path !! Managed by !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| /usr/local/etc/nginx/vhosts/dereckson.be/zed51.conf || Salt :: webserver-alkane&lt;br /&gt;
|-&lt;br /&gt;
| /var/51-wwwroot/zed/ || Salt :: wwwroot-51 (still to do) || Git repo: git@github.com:dereckson/zed.git&lt;br /&gt;
|-&lt;br /&gt;
| /var/dataroot/zed/content/ || Manually || Git repo: git@github.com:hypership/content.git&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
* nginx: &amp;lt;code&amp;gt;tail -n 100 -f /var/log/www/dereckson.be/zed51-error.log&amp;lt;/code&amp;gt;&lt;br /&gt;
* PHP: &amp;lt;code&amp;gt;tail -n 100 -f /var/log/www/dereckson.be/zed51-php.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Staging  ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note. Keruald is currently symlinked from the monorepo to vendor/keruald.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To recreate Zed staging area:&lt;br /&gt;
&lt;br /&gt;
    $ cd /var/dataroot/zed/content&lt;br /&gt;
    $ git init .&lt;br /&gt;
    $ git config --global --add safe.directory /var/dataroot/zed/content&lt;br /&gt;
    $ git remote add origin git@github.com:hypership/content.git&lt;br /&gt;
    $ git fetch --all&lt;br /&gt;
    $ git reset --hard origin/main&lt;br /&gt;
    $ git clone git@github.com:hypership/content_users.git users&lt;br /&gt;
    &lt;br /&gt;
    $ sudo -u web-be-dereckson-zed51 mkdir /var/dataroot/zed/cache/sessions&lt;br /&gt;
    $ sudo mkdir -p /var/dataroot/zed/content/users/_photos/tn&lt;br /&gt;
    $ sudo chown -R web-be-dereckson-zed51:dereckson /var/dataroot/zed/content/users/_photos&lt;br /&gt;
    $ sudo chmod 771 /var/dataroot/zed/content/users/_photos /var/dataroot/zed/content/users/_photos/tn&lt;br /&gt;
    &lt;br /&gt;
    $ mkdir -p /home/dereckson/dev/zed/hypership&lt;br /&gt;
    $ cd /home/dereckson/dev/zed/hypership&lt;br /&gt;
    $ ln -s /var/dataroot/zed/content&lt;br /&gt;
    &lt;br /&gt;
Note: Zed currently not defined in wwwroot-51 in rOPS, should be done for /var/51-wwwroot/zed&lt;br /&gt;
&lt;br /&gt;
== IRC ==&lt;br /&gt;
&lt;br /&gt;
=== Troubleshoot ===&lt;br /&gt;
==== Error loading module proxy/irc ====&lt;br /&gt;
When trying to load proxy module, I got:&lt;br /&gt;
&lt;br /&gt;
    22:00:09 -!- Irssi: Error loading module proxy/irc: /home/dereckson/.irssi/modules/libirc_proxy.so: Undefined symbol &amp;quot;g_input_add&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Proxy is now compiled by default, personal module needs to be deleted.&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2572</id>
		<title>User:Dereckson/timeless.css</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/timeless.css&amp;diff=2572"/>
		<updated>2026-08-22T19:21:14Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: Import GPT 5.6-Sol experiment&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/*  -------------------------------------------------------------&lt;br /&gt;
    T-rex and mountains are better than cats - content background&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
	background-image: url(https://windriver.nasqueron.org/~dereckson/gfx/svg/t-rex.svg) !important;&lt;br /&gt;
	background-position: 30em 10em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  -------------------------------------------------------------&lt;br /&gt;
    Tiles&lt;br /&gt;
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    */&lt;br /&gt;
&lt;br /&gt;
.tiles {&lt;br /&gt;
  grid-template-columns: 100px 50px 100px;&lt;br /&gt;
  grid-template-rows: 80px auto 80px; &lt;br /&gt;
  column-gap: 10px;&lt;br /&gt;
  row-gap: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Nasqueron colour treatment for MediaWiki&#039;s Timeless skin.&lt;br /&gt;
 *&lt;br /&gt;
 * Installation: paste this file into MediaWiki:Timeless.css.&lt;br /&gt;
 * Scope: presentation only; no Timeless templates or PHP are replaced.&lt;br /&gt;
 * Palette study: 2026-08.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
:root {&lt;br /&gt;
  --nq-fig: #383c49;&lt;br /&gt;
  --nq-dive: #376167;&lt;br /&gt;
  --nq-blue-box: #8ac5ba;&lt;br /&gt;
  --nq-grenadine: #d66972;&lt;br /&gt;
  --nq-kiss: #813235;&lt;br /&gt;
  --nq-paper: #f7f4ec;&lt;br /&gt;
  --nq-surface: #fffdf8;&lt;br /&gt;
  --nq-mist: #dce9e5;&lt;br /&gt;
  --nq-night: #20242d;&lt;br /&gt;
  --nq-line: rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Light mode: calm paper, strong editorial contrast. */&lt;br /&gt;
.skin-timeless {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container {&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 8px 24px rgba(56, 60, 73, 0.16);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-header-container a,&lt;br /&gt;
.skin-timeless #mw-header-container #p-logo-text a {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #simpleSearch {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.24);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 255, 255, 0.09);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #searchInput::placeholder {&lt;br /&gt;
  color: rgba(247, 244, 236, 0.62);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content-container {&lt;br /&gt;
  border-bottom-color: var(--nq-blue-box);&lt;br /&gt;
  background-color: #ebe6da;&lt;br /&gt;
  background-image: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless #mw-content {&lt;br /&gt;
  border: 1px solid var(--nq-line);&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  background: var(--nq-surface);&lt;br /&gt;
  box-shadow: 0 18px 48px rgba(56, 60, 73, 0.11);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: var(--nq-line);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  background: rgba(255, 253, 248, 0.72);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .sidebar-chunk h2,&lt;br /&gt;
.skin-timeless .sidebar-chunk h3 {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
  letter-spacing: 0.08em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a,&lt;br /&gt;
.skin-timeless .mw-parser-output a.external,&lt;br /&gt;
.skin-timeless .mw-parser-output a.extiw {&lt;br /&gt;
  color: var(--nq-dive);&lt;br /&gt;
  text-decoration-thickness: 1px;&lt;br /&gt;
  text-underline-offset: 0.16em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a:visited {&lt;br /&gt;
  color: #5d5367;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless a.new,&lt;br /&gt;
.skin-timeless .new a {&lt;br /&gt;
  color: var(--nq-kiss);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
  color: var(--nq-fig);&lt;br /&gt;
  letter-spacing: -0.025em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-body .mw-heading1,&lt;br /&gt;
.skin-timeless .mw-body h1,&lt;br /&gt;
.skin-timeless .mw-body .mw-heading2,&lt;br /&gt;
.skin-timeless .mw-body h2,&lt;br /&gt;
.skin-timeless .mw-body .toctitle {&lt;br /&gt;
  border-bottom-color: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .toc,&lt;br /&gt;
.skin-timeless .toccolours,&lt;br /&gt;
.skin-timeless .thumbinner,&lt;br /&gt;
.skin-timeless .wikitable,&lt;br /&gt;
.skin-timeless code,&lt;br /&gt;
.skin-timeless pre,&lt;br /&gt;
.skin-timeless .mw-code {&lt;br /&gt;
  border-color: rgba(55, 97, 103, 0.22);&lt;br /&gt;
  background: #eff3ef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .wikitable th,&lt;br /&gt;
.skin-timeless .mw_metadata th {&lt;br /&gt;
  background: var(--nq-mist);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .tools-inline li.selected {&lt;br /&gt;
  border-bottom-color: var(--nq-grenadine);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .color-left { background: var(--nq-kiss); }&lt;br /&gt;
.skin-timeless .color-middle { background: var(--nq-dive); }&lt;br /&gt;
.skin-timeless .color-right { background: var(--nq-blue-box); }&lt;br /&gt;
&lt;br /&gt;
.skin-timeless .mw-footer-container {&lt;br /&gt;
  border-top-color: rgba(247, 244, 236, 0.16);&lt;br /&gt;
  background: var(--nq-fig);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.skin-timeless :is(a, button, input, select, textarea):focus-visible {&lt;br /&gt;
  outline: 3px solid var(--nq-grenadine);&lt;br /&gt;
  outline-offset: 3px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Explicit MediaWiki night preference. */&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless {&lt;br /&gt;
  background: #171a20;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content-container,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content-container {&lt;br /&gt;
  background-color: #171a20;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless #mw-content,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless #mw-content {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: var(--nq-night);&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
  box-shadow: 0 22px 55px rgba(0, 0, 0, 0.26);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .sidebar-chunk,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .sidebar-chunk {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless a,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless a {&lt;br /&gt;
  color: var(--nq-blue-box);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless .mw-body h1.firstHeading,&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless .mw-body h1.firstHeading {&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html.skin-theme-clientpref-night .skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code),&lt;br /&gt;
html.skin-theme-clientpref-night.skin-timeless :is(.toc, .toccolours, .thumbinner, .wikitable, code, pre, .mw-code) {&lt;br /&gt;
  border-color: rgba(247, 244, 236, 0.13);&lt;br /&gt;
  background: #292e38;&lt;br /&gt;
  color: var(--nq-paper);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Follow the operating system only when MediaWiki&#039;s preference is &amp;quot;automatic&amp;quot;. */&lt;br /&gt;
@media (prefers-color-scheme: dark) {&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless {&lt;br /&gt;
    background: #171a20;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content-container,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content-container {&lt;br /&gt;
    background-color: #171a20;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless #mw-content,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless #mw-content {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: var(--nq-night);&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless .sidebar-chunk,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless .sidebar-chunk {&lt;br /&gt;
    border-color: rgba(247, 244, 236, 0.11);&lt;br /&gt;
    background: #292e38;&lt;br /&gt;
    color: var(--nq-paper);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  html.skin-theme-clientpref-os .skin-timeless a,&lt;br /&gt;
  html.skin-theme-clientpref-os.skin-timeless a {&lt;br /&gt;
    color: var(--nq-blue-box);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (prefers-reduced-motion: reduce) {&lt;br /&gt;
  .skin-timeless * {&lt;br /&gt;
    scroll-behavior: auto !important;&lt;br /&gt;
    transition-duration: 0.01ms !important;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Router&amp;diff=2571</id>
		<title>Operations grimoire/Router</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Router&amp;diff=2571"/>
		<updated>2026-08-22T18:20:49Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Routers */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Routers =&lt;br /&gt;
&lt;br /&gt;
== Checklist for new router IP ==&lt;br /&gt;
* SPF record for nasqueron.org and other mail using our system&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
&lt;br /&gt;
=== Switch primary router ===&lt;br /&gt;
&lt;br /&gt;
This procedure can be used when the current primary router doesn&#039;t allow access and network access through the GRE/CARP setup is degraded.&lt;br /&gt;
&lt;br /&gt;
Typical symptoms observed:&lt;br /&gt;
* ICMP may still work, but TCP sessions cannot actually be used&lt;br /&gt;
* `nc` can connect to TCP ports, but SSH or Vault requests hang&lt;br /&gt;
* Vault queries time out during TLS handshake&lt;br /&gt;
* access through Windriver becomes unstable&lt;br /&gt;
* router-003 does not appear to restore SSH access properly when it becomes primary&lt;br /&gt;
* the issue may disappear temporarily after switching the primary router but can come back later&lt;br /&gt;
&lt;br /&gt;
Observed troubleshooting notes:&lt;br /&gt;
* the issue can reappear after about 1 hour, but sometimes only after up to 48 hours&lt;br /&gt;
* GRE tunnels may remain pingable while application access times out&lt;br /&gt;
* when the issue occurs, routing and primary router state should be checked&lt;br /&gt;
&lt;br /&gt;
==== Procedure ====&lt;br /&gt;
&lt;br /&gt;
If `router-002` is currently primary and needs to be switched out temporarily, disable `vmx1` on `router-002` so that `router-003` can take over:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ifconfig vmx1 down on router-002 / ifconfig vmx1 up on router-003&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the unused router needs to be in INIT status and not BAKCUP, as BACKUP status blocks access for an unknown reason.&lt;br /&gt;
&lt;br /&gt;
Check CARP logs after finishing the procedure.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
tail -f /var/log/carp.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is currently a workaround, not a permanent fix.&lt;br /&gt;
&lt;br /&gt;
The network issue is still unstable and requires further investigation.&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/External_services&amp;diff=2570</id>
		<title>Operations grimoire/External services</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/External_services&amp;diff=2570"/>
		<updated>2026-08-22T12:50:06Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Amazon Web Services (AWS) ==&lt;br /&gt;
S3, account managed by Sandlayth.&lt;br /&gt;
&lt;br /&gt;
== Docker Hub ==&lt;br /&gt;
Organization account, administrative access by Sandlayth and Dereckson.&lt;br /&gt;
&lt;br /&gt;
2021-09-12: request for open source as part of the https://www.docker.com/blog/expanded-support-for-open-source-software-projects done, mainly as automated builds have been disabled in the free team plan.&lt;br /&gt;
&lt;br /&gt;
== GeoLite2 ==&lt;br /&gt;
Account at MaxMind for GeoLite2 databases, created by Dereckson.&lt;br /&gt;
&lt;br /&gt;
Account credentials stored in Vault under &amp;lt;code&amp;gt;ops/infra/maxmind&amp;lt;/code&amp;gt;.&lt;br /&gt;
There aren&#039;t the API credentials, for example Sentry ones are stored at &amp;lt;code&amp;gt;ops/secrets/nasqueron/sentry/geoipupdate&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Gandi ==&lt;br /&gt;
Some domains names:&lt;br /&gt;
* eglide.org, account managed by Sandlayth&lt;br /&gt;
* ook.space, account managed by Dereckson&lt;br /&gt;
&lt;br /&gt;
== GitHub ==&lt;br /&gt;
;Organization account&lt;br /&gt;
:Administrative access by Sandlayth and Dereckson&lt;br /&gt;
:&#039;&#039;Some OAuth could be on Dereckson account. When found, should be switched to Nasqueron (see [https://devcentral.nasqueron.org/T1109 T1109]).&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Alken-Orin&lt;br /&gt;
:As automated agent, there is an &#039;&#039;&#039;Alken-Orin&#039;&#039;&#039; account to link GitHub to CI/CD.&lt;br /&gt;
:This account is also used to fetch code from GitHub through the `staging_private_repository` Salt state. To that end, the Salt deploy SSH key is added to this account.&lt;br /&gt;
:Credentials at [https://devcentral.nasqueron.org/K53 K53].&lt;br /&gt;
&lt;br /&gt;
== Google ==&lt;br /&gt;
OAuth for DevCentral, account managed by Dereckson (under espace-win.org Google Apps organization)&lt;br /&gt;
&lt;br /&gt;
== Hurricane Electric ==&lt;br /&gt;
IPv6 connectivity, DNS for nasqueron.org: account &#039;nasqueron&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; DNS for some other domains are currently handled on the &#039;dereckson&#039; account, they&#039;ll be moved to &#039;nasqueron&#039; account when defined as secondary.&lt;br /&gt;
&lt;br /&gt;
== instances.mastodon.xyz ==&lt;br /&gt;
Credentials for this account are stored in [https://devcentral.nasqueron.org/K103 K103].&lt;br /&gt;
&lt;br /&gt;
== JetBrains ==&lt;br /&gt;
Licenses for open-source contributions in the nasqueron organization.&lt;br /&gt;
&lt;br /&gt;
Have access as org admins: Dereckson, Dorian.&lt;br /&gt;
&lt;br /&gt;
== Mailgun ==&lt;br /&gt;
Account managed by Dereckson.&lt;br /&gt;
&lt;br /&gt;
2020-02-28: account upgraded to Flex, as it&#039;s not possible anymore to add custom domains to the free plan. From July, mails will cost 0.80$ / 1000. To avoid any financial abuse, (i) a 1000 mail accounts limit is currently in (we can update it in account settings) (ii) the credit card associated for this account is limited to 5 € / month.&lt;br /&gt;
&lt;br /&gt;
== Microsoft ==&lt;br /&gt;
=== Reporting for mail sent to Outlook.com ===&lt;br /&gt;
For mail reporting, Outlook.com offers a &amp;quot;[https://sendersupport.olc.protection.outlook.com/ Smart Network Data Service]&amp;quot; to report statistics about abused IP.&lt;br /&gt;
&lt;br /&gt;
Any op can subscribe with their own Microsoft account on https://sendersupport.olc.protection.outlook.com/snds/addnetwork.aspx / authorization request should ideally be sent to postmaster for tracking.&lt;br /&gt;
&lt;br /&gt;
It&#039;s also configured to send report about mails marked as spam by Outlook users to postmaster.&lt;br /&gt;
&lt;br /&gt;
== Mural ==&lt;br /&gt;
Mural non-profit plan for Wolfplex Hackerspace ASBL, Sébastien has access.&lt;br /&gt;
&lt;br /&gt;
Personal notes:&lt;br /&gt;
* We have 10 seats&lt;br /&gt;
* To add an user: [https://app.mural.co/t/nasqueron8788/settings/members workspace &amp;gt; settings &amp;gt; members]&lt;br /&gt;
* Mail about access requests go to Wolfplex mail&lt;br /&gt;
&lt;br /&gt;
== PURL ==&lt;br /&gt;
/NET/Nasqueron is managed by the Dereckson Internet Archive account.&lt;br /&gt;
&lt;br /&gt;
== Online / Scaleway ==&lt;br /&gt;
;Online&lt;br /&gt;
* [[Ysul]], account managed by Dereckson, billed to Dereckson&lt;br /&gt;
* [[WindRiver]], account managed by Dereckson, billed to Trantorium&lt;br /&gt;
;Scaleway&lt;br /&gt;
* [[Eglide]], account managed by Sandlayth, billed to Sandlayth&lt;br /&gt;
** Following organization account introduction, Dereckson has now access too.&lt;br /&gt;
&lt;br /&gt;
== OVH / SoYouStart ==&lt;br /&gt;
Servers:&lt;br /&gt;
* [[hyper-001]], account managed by Dereckson, billed to ?&lt;br /&gt;
* [[CloudHugger]], account managed by Dereckson, billed to Trantorium&lt;br /&gt;
&lt;br /&gt;
Registrar for nasqueron.org and nasqueron-user-content.org&lt;br /&gt;
&lt;br /&gt;
== Sendgrid ==&lt;br /&gt;
Account managed by Dereckson.&lt;br /&gt;
&lt;br /&gt;
== Stud&#039;in ==&lt;br /&gt;
Account managed by Dereckson.&lt;br /&gt;
&lt;br /&gt;
== Wikimedia ==&lt;br /&gt;
OAuth authentication, managed by Dereckson&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2567</id>
		<title>User:Dereckson/Arcane Grove/BlueDrake</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2567"/>
		<updated>2026-08-08T22:09:05Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;BlueDrake&#039;&#039;&#039; is the name of the hybrid productivity-gaming workstation.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== Components ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ BlueDrake parts&lt;br /&gt;
|-&lt;br /&gt;
! Category !! Brand !! Model !! Links&lt;br /&gt;
|-&lt;br /&gt;
| Case || Lian Li || [https://lian-li.com/product/lancool-iii/ Lancool III] || [https://drive.google.com/file/d/1kd9ku_raF2uXWeLD7voGfT4ZZ10XOizy/view?usp=sharing 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Motherboard || Asus || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/ ROG Strix B650E-E Gaming WiFi] || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_manual/ 📖] [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_download/ 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU || AMD || [https://www.amd.com/en/products/processors/desktops/ryzen/7000-series/amd-ryzen-9-7900x.html Ryzen 9 7900X] || [https://www.amd.com/en/support/downloads/drivers.html/processors/ryzen/ryzen-7000-series/amd-ryzen-9-7900x.html 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU Cooler || Noctua || [https://noctua.at/en/nh-d15s NH-D15S] || [https://noctua.at/en/nh-d15s/manuals 📖]&lt;br /&gt;
|-&lt;br /&gt;
| GPU || Sapphire / AMD || PURE Radeon RX 7700 XT 12GB || [https://www.sapphiretech.com/en/consumer/pure-radeon-rx-7700-xt-12g-gddr6-frostpunk2 📖]&lt;br /&gt;
|-&lt;br /&gt;
| RAM || G.Skill || Trident Z Neo DDR5 96 GB (2×48 GB) || [https://www.gskill.com/product/165/374/1682577390/F5-5600J4040D48GX2-TZ5RK 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Storage || Kingston || Fury Renegade 2 TB NVMe M.2 (×2) || [https://www.kingston.com/en/ssd/gaming/fury-renegade-nvme-pcie-ssd 📖]&lt;br /&gt;
|-&lt;br /&gt;
| PSU || Seasonic || Core GX-750 || [https://seasonic.com/core-gx#specification 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || Alienware || AW3425DWM || [https://dl.dell.com/content/manual56809116-alienware-34-gaming-monitor-aw3425dwm-quick-start-guide.pdf?language=en-us 📖]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
=== Fedora partition ===&lt;br /&gt;
* Blocked at Fedora 42, we&#039;ve still some software incompatible with Wayland&lt;br /&gt;
* Configuration as code: https://github.com/dereckson/wynter-salt-workstation&lt;br /&gt;
&lt;br /&gt;
=== FreeBSD partition ===&lt;br /&gt;
Things to check for GPU:&lt;br /&gt;
* Ollama on FreeBSD -&amp;gt; https://www.reddit.com/r/freebsd/comments/1d3hmmz/comment/lqiulx9/ / https://github.com/ollama/ollama/pull/4172 (&amp;lt;code&amp;gt;pkg install go122 cmake vulkan-headers vulkan-loader&amp;lt;/code&amp;gt;)&lt;br /&gt;
* GPU passthrough with bhyve -&amp;gt; https://wiki.freebsd.org/bhyve/pci_passthru&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2566</id>
		<title>User:Dereckson/Arcane Grove/BlueDrake</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2566"/>
		<updated>2026-08-05T01:57:11Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Components */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;BlueDrake&#039;&#039;&#039; is the name of the hybrid productivity-gaming workstation.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== Components ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ BlueDrake parts&lt;br /&gt;
|-&lt;br /&gt;
! Category !! Brand !! Model !! Links&lt;br /&gt;
|-&lt;br /&gt;
| Case || Lian Li || [https://lian-li.com/product/lancool-iii/ Lancool III] || [https://drive.google.com/file/d/1kd9ku_raF2uXWeLD7voGfT4ZZ10XOizy/view?usp=sharing 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Motherboard || Asus || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/ ROG Strix B650E-E Gaming WiFi] || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_manual/ 📖] [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_download/ 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU || AMD || [https://www.amd.com/en/products/processors/desktops/ryzen/7000-series/amd-ryzen-9-7900x.html Ryzen 9 7900X] || [https://www.amd.com/en/support/downloads/drivers.html/processors/ryzen/ryzen-7000-series/amd-ryzen-9-7900x.html 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU Cooler || Noctua || [https://noctua.at/en/nh-d15s NH-D15S] || [https://noctua.at/en/nh-d15s/manuals 📖]&lt;br /&gt;
|-&lt;br /&gt;
| GPU || Sapphire / AMD || PURE Radeon RX 7700 XT 12GB || [https://www.sapphiretech.com/en/consumer/pure-radeon-rx-7700-xt-12g-gddr6-frostpunk2 📖]&lt;br /&gt;
|-&lt;br /&gt;
| RAM || G.Skill || Trident Z Neo DDR5 96 GB (2×48 GB) || [https://www.gskill.com/product/165/374/1682577390/F5-5600J4040D48GX2-TZ5RK 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Storage || Kingston || Fury Renegade 2 TB NVMe M.2 (×2) || [https://www.kingston.com/en/ssd/gaming/fury-renegade-nvme-pcie-ssd 📖]&lt;br /&gt;
|-&lt;br /&gt;
| PSU || Seasonic || Core GX-750 || [https://seasonic.com/core-gx#specification 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || Alienware || AW3425DWM || [https://dl.dell.com/content/manual56809116-alienware-34-gaming-monitor-aw3425dwm-quick-start-guide.pdf?language=en-us 📖]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Things to check for GPU:&lt;br /&gt;
* Ollama on FreeBSD -&amp;gt; https://www.reddit.com/r/freebsd/comments/1d3hmmz/comment/lqiulx9/ / https://github.com/ollama/ollama/pull/4172 (&amp;lt;code&amp;gt;pkg install go122 cmake vulkan-headers vulkan-loader&amp;lt;/code&amp;gt;)&lt;br /&gt;
* GPU passthrough with bhyve -&amp;gt; https://wiki.freebsd.org/bhyve/pci_passthru&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2565</id>
		<title>User:Dereckson/Arcane Grove/BlueDrake</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2565"/>
		<updated>2026-08-05T01:54:47Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Components */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;BlueDrake&#039;&#039;&#039; is the name of the hybrid productivity-gaming workstation.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== Components ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ BlueDrake parts&lt;br /&gt;
|-&lt;br /&gt;
! Category !! Brand !! Model !! Links&lt;br /&gt;
|-&lt;br /&gt;
| Case || Lian Li || [https://lian-li.com/product/lancool-iii/ Lancool III] || [https://drive.google.com/file/d/1kd9ku_raF2uXWeLD7voGfT4ZZ10XOizy/view?usp=sharing 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Motherboard || Asus || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/ ROG Strix B650E-E Gaming WiFi] || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_manual/ 📖] [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_download/ 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU || AMD || Ryzen 9 7900X || [https://www.amd.com/en/products/cpu/amd-ryzen-9-7900x 📖]&lt;br /&gt;
|-&lt;br /&gt;
| CPU Cooler || Noctua || [https://noctua.at/en/nh-d15s NH-D15S] || [https://noctua.at/en/nh-d15s/manuals 📖]&lt;br /&gt;
|-&lt;br /&gt;
| GPU || Sapphire / AMD || PURE Radeon RX 7700 XT 12GB || [https://www.sapphiretech.com/en/consumer/pure-radeon-rx-7700-xt-12g-gddr6-frostpunk2 📖]&lt;br /&gt;
|-&lt;br /&gt;
| RAM || G.Skill || Trident Z Neo DDR5 96 GB (2×48 GB) || [https://www.gskill.com/product/165/374/1682577390/F5-5600J4040D48GX2-TZ5RK 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Storage || Kingston || Fury Renegade 2 TB NVMe M.2 (×2) || [https://www.kingston.com/en/ssd/gaming/fury-renegade-nvme-pcie-ssd 📖]&lt;br /&gt;
|-&lt;br /&gt;
| PSU || Seasonic || Core GX-750 || [https://seasonic.com/core-gx#specification 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || AOC || C27G1 || [https://aoc.com/be-fr/gaming/products/monitors/c27g1/downloads 📥]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || Alienware || AW3425DWM || [https://dl.dell.com/content/manual56809116-alienware-34-gaming-monitor-aw3425dwm-quick-start-guide.pdf?language=en-us 📖]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Things to check for GPU:&lt;br /&gt;
* Ollama on FreeBSD -&amp;gt; https://www.reddit.com/r/freebsd/comments/1d3hmmz/comment/lqiulx9/ / https://github.com/ollama/ollama/pull/4172 (&amp;lt;code&amp;gt;pkg install go122 cmake vulkan-headers vulkan-loader&amp;lt;/code&amp;gt;)&lt;br /&gt;
* GPU passthrough with bhyve -&amp;gt; https://wiki.freebsd.org/bhyve/pci_passthru&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2564</id>
		<title>User:Dereckson/Arcane Grove/BlueDrake</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2564"/>
		<updated>2026-08-05T01:54:09Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Components */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;BlueDrake&#039;&#039;&#039; is the name of the hybrid productivity-gaming workstation.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== Components ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ BlueDrake parts&lt;br /&gt;
|-&lt;br /&gt;
! Category !! Brand !! Model !! Links&lt;br /&gt;
|-&lt;br /&gt;
| Case || Lian Li || [https://lian-li.com/product/lancool-iii/ Lancool III] || [https://drive.google.com/file/d/1kd9ku_raF2uXWeLD7voGfT4ZZ10XOizy/view?usp=sharing 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Motherboard || Asus || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/ ROG Strix B650E-E Gaming WiFi] || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_manual/ 📖] [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_download/ 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU || AMD || Ryzen 9 7900X || [https://www.amd.com/en/products/cpu/amd-ryzen-9-7900x 📖]&lt;br /&gt;
|-&lt;br /&gt;
| CPU Cooler || Noctua || NH-D15S || [https://noctua.at/en/nh-d15s 📝] [https://noctua.at/en/nh-d15s/manuals 📖]&lt;br /&gt;
|-&lt;br /&gt;
| GPU || Sapphire / AMD || PURE Radeon RX 7700 XT 12GB || [https://www.sapphiretech.com/en/consumer/pure-radeon-rx-7700-xt-12g-gddr6-frostpunk2 📖]&lt;br /&gt;
|-&lt;br /&gt;
| RAM || G.Skill || Trident Z Neo DDR5 96 GB (2×48 GB) || [https://www.gskill.com/product/165/374/1682577390/F5-5600J4040D48GX2-TZ5RK 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Storage || Kingston || Fury Renegade 2 TB NVMe M.2 (×2) || [https://www.kingston.com/en/ssd/gaming/fury-renegade-nvme-pcie-ssd 📖]&lt;br /&gt;
|-&lt;br /&gt;
| PSU || Seasonic || Core GX-750 || [https://seasonic.com/core-gx#specification 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || AOC || C27G1 || [https://aoc.com/be-fr/gaming/products/monitors/c27g1/downloads 📥]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || Alienware || AW3425DWM || [https://dl.dell.com/content/manual56809116-alienware-34-gaming-monitor-aw3425dwm-quick-start-guide.pdf?language=en-us 📖]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Things to check for GPU:&lt;br /&gt;
* Ollama on FreeBSD -&amp;gt; https://www.reddit.com/r/freebsd/comments/1d3hmmz/comment/lqiulx9/ / https://github.com/ollama/ollama/pull/4172 (&amp;lt;code&amp;gt;pkg install go122 cmake vulkan-headers vulkan-loader&amp;lt;/code&amp;gt;)&lt;br /&gt;
* GPU passthrough with bhyve -&amp;gt; https://wiki.freebsd.org/bhyve/pci_passthru&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2563</id>
		<title>User:Dereckson/Arcane Grove/BlueDrake</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=User:Dereckson/Arcane_Grove/BlueDrake&amp;diff=2563"/>
		<updated>2026-08-05T01:53:04Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Components */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;BlueDrake&#039;&#039;&#039; is the name of the hybrid productivity-gaming workstation.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== Components ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ BlueDrake parts&lt;br /&gt;
|-&lt;br /&gt;
! Category !! Brand !! Model !! Links&lt;br /&gt;
|-&lt;br /&gt;
| Case || Lian Li || [https://lian-li.com/product/lancool-iii/ Lancool III] || [https://drive.google.com/file/d/1kd9ku_raF2uXWeLD7voGfT4ZZ10XOizy/view?usp=sharing 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Motherboard || Asus || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/ ROG Strix B650E-E Gaming WiFi] || [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_manual/ 📖] [https://rog.asus.com/motherboards/rog-strix/rog-strix-b650e-e-gaming-wifi-model/helpdesk_download/ 📥]&lt;br /&gt;
|-&lt;br /&gt;
| CPU || AMD || Ryzen 9 7900X || [https://www.amd.com/en/products/cpu/amd-ryzen-9-7900x 📖]&lt;br /&gt;
|-&lt;br /&gt;
| CPU Cooler || Noctua || NH-D15S || [https://noctua.at/en/nh-d15s 📖] [https://noctua.at/en/nh-d15s/manuals&lt;br /&gt;
|-&lt;br /&gt;
| GPU || Sapphire / AMD || PURE Radeon RX 7700 XT 12GB || [https://www.sapphiretech.com/en/consumer/pure-radeon-rx-7700-xt-12g-gddr6-frostpunk2 📖]&lt;br /&gt;
|-&lt;br /&gt;
| RAM || G.Skill || Trident Z Neo DDR5 96 GB (2×48 GB) || [https://www.gskill.com/product/165/374/1682577390/F5-5600J4040D48GX2-TZ5RK 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Storage || Kingston || Fury Renegade 2 TB NVMe M.2 (×2) || [https://www.kingston.com/en/ssd/gaming/fury-renegade-nvme-pcie-ssd 📖]&lt;br /&gt;
|-&lt;br /&gt;
| PSU || Seasonic || Core GX-750 || [https://seasonic.com/core-gx#specification 📖]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || AOC || C27G1 || [https://aoc.com/be-fr/gaming/products/monitors/c27g1/downloads 📥]&lt;br /&gt;
|-&lt;br /&gt;
| Screen || Alienware || AW3425DWM || [https://dl.dell.com/content/manual56809116-alienware-34-gaming-monitor-aw3425dwm-quick-start-guide.pdf?language=en-us 📖]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
=== FreeBSD ===&lt;br /&gt;
Things to check for GPU:&lt;br /&gt;
* Ollama on FreeBSD -&amp;gt; https://www.reddit.com/r/freebsd/comments/1d3hmmz/comment/lqiulx9/ / https://github.com/ollama/ollama/pull/4172 (&amp;lt;code&amp;gt;pkg install go122 cmake vulkan-headers vulkan-loader&amp;lt;/code&amp;gt;)&lt;br /&gt;
* GPU passthrough with bhyve -&amp;gt; https://wiki.freebsd.org/bhyve/pci_passthru&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2562</id>
		<title>Operations grimoire/Deploy with Terraform</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Deploy_with_Terraform&amp;diff=2562"/>
		<updated>2026-07-23T13:19:56Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Troubleshoot */ Document tofu state rm&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Where to work? ==&lt;br /&gt;
We deploy from [[Complector]] using &amp;lt;code&amp;gt;/opt/salt/nasqueron-operations&amp;lt;/code&amp;gt; as our local copy of rOPS, authoritative for both Salt and Terraform&lt;br /&gt;
&lt;br /&gt;
You need to belong to the &amp;lt;code&amp;gt;ops&amp;lt;/code&amp;gt; group to be able to have access and have write-rights on the repository&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to work from there to save a shared Terraform state.&lt;br /&gt;
&lt;br /&gt;
== Specific deployment notes ==&lt;br /&gt;
=== Vault / OpenBao ===&lt;br /&gt;
==== General notes ====&lt;br /&gt;
;OpenTofu support&lt;br /&gt;
As of 2026-02-07, the Vault provider isn&#039;t compiled for FreeBSD. You need to use Terraform instead.&lt;br /&gt;
&lt;br /&gt;
;Vault&lt;br /&gt;
You need a Vault token to allow the provider to connect.&lt;br /&gt;
&lt;br /&gt;
You also need to set VAULT_ADDR to https://172.27.27.7:8200 as Vault doesn&#039;t listen on 127.0.0.1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ export VAULT_ADDR=https://172.27.27.7:8200&lt;br /&gt;
$ sudo /opt/salt/nasqueron-operations/utils/vault/issue-admin-token.py &amp;gt; ~/.vault-token&lt;br /&gt;
    &lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It could be interesting to maintain an alternative policy to admin for the Terraform provider, restricted to the current actions. Note benefits would be limited as those include policies management.&lt;br /&gt;
&lt;br /&gt;
==== Propagate secrets (DRP) ====&lt;br /&gt;
;No automatic secret rotation&lt;br /&gt;
Secrets rotation is disabled with a lifecycle management &amp;lt;code&amp;gt;ignore_changes = [ secret_id, ]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To rotate a secret, it needs first to be destroyed from terraform state:&lt;br /&gt;
&amp;lt;code&amp;gt;terraform destroy -target=module.viperserv_approle.vault_approle_auth_backend_role_secret_id.this&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Full procedure &lt;br /&gt;
Once the AppRole have been created in Vault, they need to be provisioned to the relevant configuration files.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations/terraform/openbao&lt;br /&gt;
$ terraform init # if you&#039;ve a new entry requiring a module, it needs to be installed&lt;br /&gt;
$ terraform plan&lt;br /&gt;
$ terraform apply&lt;br /&gt;
&lt;br /&gt;
$ cd /opt/salt/nasqueron-operations&lt;br /&gt;
$ salt windriver state.sls_id /usr/local/etc/secrets/rhyne-wyse.yaml roles/reports/rhyne-wyse/config&lt;br /&gt;
$ salt windriver state.sls_id /srv/viperserv/.credentials roles/viperserv/eggdrop/config&lt;br /&gt;
# Missing for router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;alert&amp;quot;&amp;gt;Important. Each time you&#039;ll reprovision the secrets, they will change.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to always apply this full procedure.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CARP OVH secrets usage ===&lt;br /&gt;
&lt;br /&gt;
The OVH credentials used by the CARP failover script are stored in Vault at:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;apps/network/carp-hyper-001-switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These credentials are accessed by router-002 and router-003.  They are then used by a script triggered by devd to update the OVH failover IP MAC address depending on the CARP state (MASTER/BACKUP). The script authenticates to Vault using AppRole (via secretsmith) to retrieve them.&lt;br /&gt;
&lt;br /&gt;
The AppRole credentials (role_id and secret_id) used by the script are stored in:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ops/secrets/network/router/vault&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vault secrets are provisioned using Terraform (openbao module), and must be propagated to the routers using Salt. &lt;br /&gt;
&lt;br /&gt;
The Vault Secrets may need to be rotated, so we need a simple way to propagate&lt;br /&gt;
updated credentials to routers. This is enabled by the mechanism&lt;br /&gt;
introduced in D4026.&lt;br /&gt;
&lt;br /&gt;
== Table of Terraform states ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Terraform and OpenBao states&lt;br /&gt;
|-&lt;br /&gt;
! Configuration !! State back-end !! Path !! Software to use&lt;br /&gt;
|-&lt;br /&gt;
| openbao || On disk || /opt/salt/nasqueron-operations/terraform/openbao/terraform.tfstate || Terraform&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On disk paths are stored in Complector.&lt;br /&gt;
&lt;br /&gt;
== Troubleshoot ==&lt;br /&gt;
=== Error: Module not installed ===&lt;br /&gt;
&lt;br /&gt;
You need to run &amp;lt;code&amp;gt;tofu init&amp;lt;/code&amp;gt; to prepare for any new provider.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu plan&lt;br /&gt;
&lt;br /&gt;
│ Error: Module not installed&lt;br /&gt;
│ &lt;br /&gt;
│   on rhyne_wyse.tf line 23:&lt;br /&gt;
│   23: module &amp;quot;rhyne_wyse_approle&amp;quot; {&lt;br /&gt;
│ &lt;br /&gt;
│ This module is not yet installed. Run &amp;quot;tofu init&amp;quot; to install all modules required by this configuration.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: Incompatible provider version ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
$ tofu init&lt;br /&gt;
&lt;br /&gt;
Initializing the backend...&lt;br /&gt;
Initializing modules...&lt;br /&gt;
- rhyne_wyse_approle in modules/app_credentials&lt;br /&gt;
&lt;br /&gt;
Initializing provider plugins...&lt;br /&gt;
- Finding hashicorp/vault versions matching &amp;quot;5.3.0&amp;quot;...&lt;br /&gt;
╷&lt;br /&gt;
│ Error: Incompatible provider version&lt;br /&gt;
│ &lt;br /&gt;
│ Provider registry.opentofu.org/hashicorp/vault v5.3.0 does not have a package available for your current platform, freebsd_amd64.&lt;br /&gt;
│ &lt;br /&gt;
│ Provider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On OpenTofu, some modules are only compiled for Linux, not for FreeBSD.&lt;br /&gt;
Switch to Terraform pending a solution to help the OpenTofu builds.&lt;br /&gt;
&lt;br /&gt;
=== State rollback ===&lt;br /&gt;
&lt;br /&gt;
When declaring new Terraform resources, it&#039;s possible to remove from the state an item, without actually destroying it at infrastructure level:&lt;br /&gt;
&lt;br /&gt;
    $ tofu state rm &amp;lt;state name&amp;gt;&lt;br /&gt;
    Removed ….…&lt;br /&gt;
    Successfully removed 1 resource instance(s).&lt;br /&gt;
&lt;br /&gt;
[[Category:Operations grimoire]]&lt;br /&gt;
[[Category:Terraform]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Operations_repository&amp;diff=2560</id>
		<title>Operations grimoire/Operations repository</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Operations_repository&amp;diff=2560"/>
		<updated>2026-07-18T20:43:06Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Python dependencies */ Works on Linux too&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Our configuration as code is stores in the operations (rOPS) repository.&lt;br /&gt;
&lt;br /&gt;
== Repository layout ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ Content of rOPS&lt;br /&gt;
|-&lt;br /&gt;
! Path !! Description&lt;br /&gt;
|-&lt;br /&gt;
| _modules || Custom execution modules&lt;br /&gt;
|-&lt;br /&gt;
| _states || Custom states modules&lt;br /&gt;
|-&lt;br /&gt;
| _tests || Unit tests for custom modules, scripts, tests for repo&lt;br /&gt;
|-&lt;br /&gt;
| hotfixes || Fixes you need to run to solve a problem on the servers&lt;br /&gt;
|-&lt;br /&gt;
| pillar || Configuration data structures to use in the states&lt;br /&gt;
|-&lt;br /&gt;
| roles || The states to deploy, divided in roles and then in units&lt;br /&gt;
|-&lt;br /&gt;
| utils || Helper scripts to maintain the repository&lt;br /&gt;
|-&lt;br /&gt;
| terraform || OpenTofu / Terraform infrastructure configuration&lt;br /&gt;
|-&lt;br /&gt;
| map.jinja || As we deploy on several OS and distros, mapping of packages names or directories&lt;br /&gt;
|-&lt;br /&gt;
| top.sls || topfile: what we deploy where?&lt;br /&gt;
|-&lt;br /&gt;
| PORTS || Documentation of the ports used in the configuration&lt;br /&gt;
|-&lt;br /&gt;
| UIDs || Documentation of the users used in the configuration&lt;br /&gt;
|-&lt;br /&gt;
| GIDs || Documentation of the groups used in the configuration&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Contributions howto ==&lt;br /&gt;
=== Repository source ===&lt;br /&gt;
You&#039;ll find the Operations repository at https://devcentral.nasqueron.org/source/operations/&lt;br /&gt;
&lt;br /&gt;
If DevCentral isn&#039;t available, a mirror of the repository can be found at https://github.com/nasqueron/operations.&lt;br /&gt;
&lt;br /&gt;
=== Workflow of contributions ===&lt;br /&gt;
We follow the general workflow is described at [[How to contribute code]].&lt;br /&gt;
&lt;br /&gt;
Before committing a change to the main branch, you can test a deployment on a server.&lt;br /&gt;
In that case, merge in main immediately after the deployment as the repository is the source of truth for the server state.&lt;br /&gt;
&lt;br /&gt;
Log what you do on #nasqueron-ops, so it will be included at https://infra.nasqueron.org/servers-log/&lt;br /&gt;
&lt;br /&gt;
=== Code conventions ===&lt;br /&gt;
&#039;&#039;See also: [[Code conventions]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Python code: run `black` before committing&lt;br /&gt;
* YAML: indent with two spaces, including list bullets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
id:&lt;br /&gt;
  method:&lt;br /&gt;
    - somekey: value&lt;br /&gt;
    - otherkey: value&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Environment setup ==&lt;br /&gt;
&lt;br /&gt;
Use of WindRiver is recommended if you wish to get an environment already installed for your contributions.&lt;br /&gt;
&lt;br /&gt;
=== Everywhere setup ===&lt;br /&gt;
This section cover topics needed both on our development servers or in your laptop.&lt;br /&gt;
&lt;br /&gt;
==== Python dependencies ====&lt;br /&gt;
&lt;br /&gt;
Create a virtual environment, then install dependencies from {{Ops file|requirements.txt}}:&lt;br /&gt;
&lt;br /&gt;
    mkdir -p $HOME/dev/python/ops&lt;br /&gt;
    python3 -m venv $HOME/dev/python/ops&lt;br /&gt;
    source $HOME/dev/python/ops/bin/activate&lt;br /&gt;
    pip install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
As of July 2026, it seems possible to also install Salt in the virtual environment and run tests:&lt;br /&gt;
&lt;br /&gt;
    pip install salt==3006.26&lt;br /&gt;
    make test&lt;br /&gt;
&lt;br /&gt;
(tested on WindRiver: with FreeBSD 15.1, Python 3.12.13, Salt 3006.26)&lt;br /&gt;
(tested on local workstation BlueDrake: with Fedora 42 Python 3.13.13, Salt 3006.26)&lt;br /&gt;
&lt;br /&gt;
==== Datacube ====&lt;br /&gt;
&lt;br /&gt;
If you&#039;re in the ops group, you&#039;ll need sometimes to deploy from branches. As our deployment server Complector doesn&#039;t have PHP installed (so no arc), you need to exchange branches through our datacube.&lt;br /&gt;
&lt;br /&gt;
On WindRiver:&lt;br /&gt;
&lt;br /&gt;
     git remote add datacube /datacube/git/operations.git&lt;br /&gt;
&lt;br /&gt;
Anywhere else:&lt;br /&gt;
&lt;br /&gt;
     git remote add datacube ssh://windriver.nasqueron.org/datacube/git/operations.git&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t in the ops groups, and have access to our devserver, you&#039;ll still be able to clone or pull from that directory, but not to write there (ie push branches).&lt;br /&gt;
&lt;br /&gt;
=== Local computer setup ===&lt;br /&gt;
&lt;br /&gt;
This section cover topics NOT needed on our development servers.&lt;br /&gt;
&lt;br /&gt;
==== macOS ====&lt;br /&gt;
&lt;br /&gt;
To run tests properly, install first:&lt;br /&gt;
&lt;br /&gt;
    brew install bats&lt;br /&gt;
    brew install python3.13&lt;br /&gt;
&lt;br /&gt;
You can now setup the virtual environment per everywhere setup instructions.&lt;br /&gt;
&lt;br /&gt;
Salt behaves correctly on macOS, so you can install it safely in the virtual environment:&lt;br /&gt;
&lt;br /&gt;
    pip install salt==3006.26&lt;br /&gt;
&lt;br /&gt;
Check the version on WindRiver or Complector with &amp;lt;code&amp;gt;salt --versions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, to run the tests, you need a recent version of make, but the BSD make currently shipped, as of Tahoe 26.5, isn&#039;t up-to-date:&lt;br /&gt;
&lt;br /&gt;
    brew install gmake&lt;br /&gt;
    (cd _tests &amp;amp;&amp;amp; gmake)&lt;br /&gt;
&lt;br /&gt;
==== Arcanist ====&lt;br /&gt;
&lt;br /&gt;
To use arc with the operations repository, our collection of patches is needed.&lt;br /&gt;
&lt;br /&gt;
If arcanist isn&#039;t installed through Git repository, best is to do like on devserver role:&lt;br /&gt;
&lt;br /&gt;
    mkdir /opt/phabricator &amp;amp;&amp;amp; cd /opt/phabricator&lt;br /&gt;
    git clone https://github.com/nasqueron/arcanist.git&lt;br /&gt;
    (cd arcanist &amp;amp;&amp;amp; git checkout production)&lt;br /&gt;
    ln -s /opt/phabricator/arcanist/bin/arc /usr/local/bin/&lt;br /&gt;
    git clone https://devcentral.nasqueron.org/source/shellcheck-linter.git&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve already cloned Arcanist repository, switch to our branch production:&lt;br /&gt;
&lt;br /&gt;
    git remote add nasqueron git@github.com:nasqueron/arcanist.git&lt;br /&gt;
    git fetch --all&lt;br /&gt;
    git checkout production&lt;br /&gt;
&lt;br /&gt;
As of August 2024, there is no plan to make that repository available on DevCentral.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll also need shellcheck library-linter library, normally at the same level than your arcanist repository.&lt;br /&gt;
&lt;br /&gt;
==== IDE configuration ====&lt;br /&gt;
[[File:PyCharm-TemplateLanguages.png|thumb|right|Screenshot of correctly configured template languages]]&lt;br /&gt;
The states .sls file are Jinja2 templates producing YAML files, or &amp;quot;jinja2+yaml&amp;quot;. If this case can&#039;t be handled by your IDE, it works best to use YAML syntax highlighting.&lt;br /&gt;
&lt;br /&gt;
If you use PyCharm or IntelliJ IDEA Ultimate, you can configure this scheme:&lt;br /&gt;
&lt;br /&gt;
* Allow YAML files to be Jinja2 templates&lt;br /&gt;
** Go to Settings &amp;gt; Languages &amp;amp; Frameworks &amp;gt; Template Languages&lt;br /&gt;
** Select Jinja2 as template language&lt;br /&gt;
** Add YAML in the list&lt;br /&gt;
* Open SLS as YAML&lt;br /&gt;
&lt;br /&gt;
== Linting ==&lt;br /&gt;
&lt;br /&gt;
=== pre-commit ===&lt;br /&gt;
You can use &#039;&#039;&#039;pre-commit&#039;&#039;&#039; you can install with &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; to get a hook running Python code to lint the repository before a commit.&lt;br /&gt;
&lt;br /&gt;
Beware if you use &amp;lt;code&amp;gt;arc diff&amp;lt;/code&amp;gt; without a commit first: your message will be lost if pre-commit raises an error, always use &amp;lt;code&amp;gt;git commit&amp;lt;/code&amp;gt; first.&lt;br /&gt;
&lt;br /&gt;
On FreeBSD, there is an issue to build with clang 16+ the ruamel yaml clib library. You can build the FreeBSD port devel/py-ruamel.yaml.clib and copy the .whl to a stable path, then install it in your virtual environment.&lt;br /&gt;
&lt;br /&gt;
On WindRiver, the .whl is available in /opt/python/py311, so you can:&lt;br /&gt;
&lt;br /&gt;
    $ python3 -m venv /path/to/your/virtualenv&lt;br /&gt;
    $ source /path/to/your/virtualenv/bin/activate&lt;br /&gt;
    $ pip install /opt/python/py311/ruamel.yaml.clib-0.2.8-cp311-cp311-freebsd_14_0_release_p3_amd64.whl&lt;br /&gt;
    $ pip install pre-commit&lt;br /&gt;
    $ make&lt;br /&gt;
    pre-commit install&lt;br /&gt;
    pre-commit installed at .git/hooks/pre-commit&lt;br /&gt;
    $ rehash&lt;br /&gt;
    $ pre-commit&lt;br /&gt;
&lt;br /&gt;
== Refactoring ==&lt;br /&gt;
&lt;br /&gt;
=== Rename a lot of files ===&lt;br /&gt;
&lt;br /&gt;
To move a directory and rename the &#039;&#039;Source file&#039;&#039; headers:&lt;br /&gt;
&lt;br /&gt;
    git mv roles/webserver-legacy/nginx/files roles/webserver-alkane/nginx/&lt;br /&gt;
    cd roles/webserver-alkane/nginx/&lt;br /&gt;
    find . -type f -name &#039;*.conf&#039; | xargs gsed -i s/webserver-legacy/webserver-alkane/g&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[/FAQ]] - Frequently asked questions about the Operations repository&lt;br /&gt;
* [[How to contribute code]]&lt;br /&gt;
* [[Devserver reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
	<entry>
		<id>https://agora.nasqueron.org/index.php?title=Operations_grimoire/Operations_repository&amp;diff=2559</id>
		<title>Operations grimoire/Operations repository</title>
		<link rel="alternate" type="text/html" href="https://agora.nasqueron.org/index.php?title=Operations_grimoire/Operations_repository&amp;diff=2559"/>
		<updated>2026-07-18T20:40:18Z</updated>

		<summary type="html">&lt;p&gt;Dereckson: /* Python dependencies */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Our configuration as code is stores in the operations (rOPS) repository.&lt;br /&gt;
&lt;br /&gt;
== Repository layout ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ Content of rOPS&lt;br /&gt;
|-&lt;br /&gt;
! Path !! Description&lt;br /&gt;
|-&lt;br /&gt;
| _modules || Custom execution modules&lt;br /&gt;
|-&lt;br /&gt;
| _states || Custom states modules&lt;br /&gt;
|-&lt;br /&gt;
| _tests || Unit tests for custom modules, scripts, tests for repo&lt;br /&gt;
|-&lt;br /&gt;
| hotfixes || Fixes you need to run to solve a problem on the servers&lt;br /&gt;
|-&lt;br /&gt;
| pillar || Configuration data structures to use in the states&lt;br /&gt;
|-&lt;br /&gt;
| roles || The states to deploy, divided in roles and then in units&lt;br /&gt;
|-&lt;br /&gt;
| utils || Helper scripts to maintain the repository&lt;br /&gt;
|-&lt;br /&gt;
| terraform || OpenTofu / Terraform infrastructure configuration&lt;br /&gt;
|-&lt;br /&gt;
| map.jinja || As we deploy on several OS and distros, mapping of packages names or directories&lt;br /&gt;
|-&lt;br /&gt;
| top.sls || topfile: what we deploy where?&lt;br /&gt;
|-&lt;br /&gt;
| PORTS || Documentation of the ports used in the configuration&lt;br /&gt;
|-&lt;br /&gt;
| UIDs || Documentation of the users used in the configuration&lt;br /&gt;
|-&lt;br /&gt;
| GIDs || Documentation of the groups used in the configuration&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Contributions howto ==&lt;br /&gt;
=== Repository source ===&lt;br /&gt;
You&#039;ll find the Operations repository at https://devcentral.nasqueron.org/source/operations/&lt;br /&gt;
&lt;br /&gt;
If DevCentral isn&#039;t available, a mirror of the repository can be found at https://github.com/nasqueron/operations.&lt;br /&gt;
&lt;br /&gt;
=== Workflow of contributions ===&lt;br /&gt;
We follow the general workflow is described at [[How to contribute code]].&lt;br /&gt;
&lt;br /&gt;
Before committing a change to the main branch, you can test a deployment on a server.&lt;br /&gt;
In that case, merge in main immediately after the deployment as the repository is the source of truth for the server state.&lt;br /&gt;
&lt;br /&gt;
Log what you do on #nasqueron-ops, so it will be included at https://infra.nasqueron.org/servers-log/&lt;br /&gt;
&lt;br /&gt;
=== Code conventions ===&lt;br /&gt;
&#039;&#039;See also: [[Code conventions]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Python code: run `black` before committing&lt;br /&gt;
* YAML: indent with two spaces, including list bullets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
id:&lt;br /&gt;
  method:&lt;br /&gt;
    - somekey: value&lt;br /&gt;
    - otherkey: value&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Environment setup ==&lt;br /&gt;
&lt;br /&gt;
Use of WindRiver is recommended if you wish to get an environment already installed for your contributions.&lt;br /&gt;
&lt;br /&gt;
=== Everywhere setup ===&lt;br /&gt;
This section cover topics needed both on our development servers or in your laptop.&lt;br /&gt;
&lt;br /&gt;
==== Python dependencies ====&lt;br /&gt;
&lt;br /&gt;
Create a virtual environment, then install dependencies from {{Ops file|requirements.txt}}:&lt;br /&gt;
&lt;br /&gt;
    mkdir -p $HOME/dev/python/ops&lt;br /&gt;
    python3 -m venv $HOME/dev/python/ops&lt;br /&gt;
    source $HOME/dev/python/ops/bin/activate&lt;br /&gt;
    pip install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
As of July 2026, it seems possible to also install Salt in the virtual environment and run tests:&lt;br /&gt;
&lt;br /&gt;
    pip install salt==3006.26&lt;br /&gt;
    make test&lt;br /&gt;
&lt;br /&gt;
(tested on WindRiver, so with FreeBSD 15.1, Python 3.12.13, Salt 3006.26)&lt;br /&gt;
&lt;br /&gt;
==== Datacube ====&lt;br /&gt;
&lt;br /&gt;
If you&#039;re in the ops group, you&#039;ll need sometimes to deploy from branches. As our deployment server Complector doesn&#039;t have PHP installed (so no arc), you need to exchange branches through our datacube.&lt;br /&gt;
&lt;br /&gt;
On WindRiver:&lt;br /&gt;
&lt;br /&gt;
     git remote add datacube /datacube/git/operations.git&lt;br /&gt;
&lt;br /&gt;
Anywhere else:&lt;br /&gt;
&lt;br /&gt;
     git remote add datacube ssh://windriver.nasqueron.org/datacube/git/operations.git&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t in the ops groups, and have access to our devserver, you&#039;ll still be able to clone or pull from that directory, but not to write there (ie push branches).&lt;br /&gt;
&lt;br /&gt;
=== Local computer setup ===&lt;br /&gt;
&lt;br /&gt;
This section cover topics NOT needed on our development servers.&lt;br /&gt;
&lt;br /&gt;
==== macOS ====&lt;br /&gt;
&lt;br /&gt;
To run tests properly, install first:&lt;br /&gt;
&lt;br /&gt;
    brew install bats&lt;br /&gt;
    brew install python3.13&lt;br /&gt;
&lt;br /&gt;
You can now setup the virtual environment per everywhere setup instructions.&lt;br /&gt;
&lt;br /&gt;
Salt behaves correctly on macOS, so you can install it safely in the virtual environment:&lt;br /&gt;
&lt;br /&gt;
    pip install salt==3006.26&lt;br /&gt;
&lt;br /&gt;
Check the version on WindRiver or Complector with &amp;lt;code&amp;gt;salt --versions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Finally, to run the tests, you need a recent version of make, but the BSD make currently shipped, as of Tahoe 26.5, isn&#039;t up-to-date:&lt;br /&gt;
&lt;br /&gt;
    brew install gmake&lt;br /&gt;
    (cd _tests &amp;amp;&amp;amp; gmake)&lt;br /&gt;
&lt;br /&gt;
==== Arcanist ====&lt;br /&gt;
&lt;br /&gt;
To use arc with the operations repository, our collection of patches is needed.&lt;br /&gt;
&lt;br /&gt;
If arcanist isn&#039;t installed through Git repository, best is to do like on devserver role:&lt;br /&gt;
&lt;br /&gt;
    mkdir /opt/phabricator &amp;amp;&amp;amp; cd /opt/phabricator&lt;br /&gt;
    git clone https://github.com/nasqueron/arcanist.git&lt;br /&gt;
    (cd arcanist &amp;amp;&amp;amp; git checkout production)&lt;br /&gt;
    ln -s /opt/phabricator/arcanist/bin/arc /usr/local/bin/&lt;br /&gt;
    git clone https://devcentral.nasqueron.org/source/shellcheck-linter.git&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve already cloned Arcanist repository, switch to our branch production:&lt;br /&gt;
&lt;br /&gt;
    git remote add nasqueron git@github.com:nasqueron/arcanist.git&lt;br /&gt;
    git fetch --all&lt;br /&gt;
    git checkout production&lt;br /&gt;
&lt;br /&gt;
As of August 2024, there is no plan to make that repository available on DevCentral.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll also need shellcheck library-linter library, normally at the same level than your arcanist repository.&lt;br /&gt;
&lt;br /&gt;
==== IDE configuration ====&lt;br /&gt;
[[File:PyCharm-TemplateLanguages.png|thumb|right|Screenshot of correctly configured template languages]]&lt;br /&gt;
The states .sls file are Jinja2 templates producing YAML files, or &amp;quot;jinja2+yaml&amp;quot;. If this case can&#039;t be handled by your IDE, it works best to use YAML syntax highlighting.&lt;br /&gt;
&lt;br /&gt;
If you use PyCharm or IntelliJ IDEA Ultimate, you can configure this scheme:&lt;br /&gt;
&lt;br /&gt;
* Allow YAML files to be Jinja2 templates&lt;br /&gt;
** Go to Settings &amp;gt; Languages &amp;amp; Frameworks &amp;gt; Template Languages&lt;br /&gt;
** Select Jinja2 as template language&lt;br /&gt;
** Add YAML in the list&lt;br /&gt;
* Open SLS as YAML&lt;br /&gt;
&lt;br /&gt;
== Linting ==&lt;br /&gt;
&lt;br /&gt;
=== pre-commit ===&lt;br /&gt;
You can use &#039;&#039;&#039;pre-commit&#039;&#039;&#039; you can install with &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; to get a hook running Python code to lint the repository before a commit.&lt;br /&gt;
&lt;br /&gt;
Beware if you use &amp;lt;code&amp;gt;arc diff&amp;lt;/code&amp;gt; without a commit first: your message will be lost if pre-commit raises an error, always use &amp;lt;code&amp;gt;git commit&amp;lt;/code&amp;gt; first.&lt;br /&gt;
&lt;br /&gt;
On FreeBSD, there is an issue to build with clang 16+ the ruamel yaml clib library. You can build the FreeBSD port devel/py-ruamel.yaml.clib and copy the .whl to a stable path, then install it in your virtual environment.&lt;br /&gt;
&lt;br /&gt;
On WindRiver, the .whl is available in /opt/python/py311, so you can:&lt;br /&gt;
&lt;br /&gt;
    $ python3 -m venv /path/to/your/virtualenv&lt;br /&gt;
    $ source /path/to/your/virtualenv/bin/activate&lt;br /&gt;
    $ pip install /opt/python/py311/ruamel.yaml.clib-0.2.8-cp311-cp311-freebsd_14_0_release_p3_amd64.whl&lt;br /&gt;
    $ pip install pre-commit&lt;br /&gt;
    $ make&lt;br /&gt;
    pre-commit install&lt;br /&gt;
    pre-commit installed at .git/hooks/pre-commit&lt;br /&gt;
    $ rehash&lt;br /&gt;
    $ pre-commit&lt;br /&gt;
&lt;br /&gt;
== Refactoring ==&lt;br /&gt;
&lt;br /&gt;
=== Rename a lot of files ===&lt;br /&gt;
&lt;br /&gt;
To move a directory and rename the &#039;&#039;Source file&#039;&#039; headers:&lt;br /&gt;
&lt;br /&gt;
    git mv roles/webserver-legacy/nginx/files roles/webserver-alkane/nginx/&lt;br /&gt;
    cd roles/webserver-alkane/nginx/&lt;br /&gt;
    find . -type f -name &#039;*.conf&#039; | xargs gsed -i s/webserver-legacy/webserver-alkane/g&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* [[/FAQ]] - Frequently asked questions about the Operations repository&lt;br /&gt;
* [[How to contribute code]]&lt;br /&gt;
* [[Devserver reference]]&lt;/div&gt;</summary>
		<author><name>Dereckson</name></author>
	</entry>
</feed>