Updated for new version

This commit is contained in:
2026-10-29 11:44:17 -04:00
parent d038fe3bab
commit 885f4160a7
11 changed files with 199 additions and 56 deletions
+60 -42
View File
@@ -5,13 +5,16 @@ Deploy the Wikideck production container to an existing Kubernetes cluster using
volume claim, a dedicated ServiceAccount, a ClusterIP Service, and a NetworkPolicy. volume claim, a dedicated ServiceAccount, a ClusterIP Service, and a NetworkPolicy.
An HTTPS Ingress is optional and disabled by default. An HTTPS Ingress is optional and disabled by default.
**Wikideck is an experimental RandomX testnet, not a production-ready public **Wikideck is mainnet-first in code but remains an experimental, launch-gated
mainnet.** The updated application has independent validating nodes, fork choice, candidate; the current production decision is NO-GO.** No public network,
native NFTs, and encrypted wallet backup/recovery. It supplies no public bootnodes bootnodes, independent audit, or production readiness is established. The
or deployed public network. Use test wallets and no valuable assets. This role application includes validating nodes, fork choice, native NFTs, and encrypted
local-wallet backup/recovery. Use test wallets and no valuable assets. This role
deploys a **validator**, never a miner: mining is an explicitly started native CLI deploys a **validator**, never a miner: mining is an explicitly started native CLI
process outside the browser. Private keys, backups and passwords must stay off process outside the browser. Private keys, backups and passwords must stay off
validator pods and out of Ansible variables. validator pods and out of Ansible variables. See the upstream
[mainnet launch gates](https://github.com/ericomeehan/wikideck/blob/main/docs/mainnet-launch.md)
and [operations/recovery guidance](https://github.com/ericomeehan/wikideck/blob/main/docs/operations-recovery.md).
## Requirements ## Requirements
@@ -24,13 +27,16 @@ validator pods and out of Ansible variables.
- Kubeconfig access to create/update/read the resources in the target namespace, - Kubeconfig access to create/update/read the resources in the target namespace,
delete the role's Ingress/NetworkPolicy when disabled, and optionally create a delete the role's Ingress/NetworkPolicy when disabled, and optionally create a
Namespace. No cluster-admin access or in-cluster workload RBAC is needed. Namespace. No cluster-admin access or in-cluster workload RBAC is needed.
- **A published image built from Wikideck's production Dockerfile.** There is no - **A published, immutable image built from Wikideck's production Dockerfile.** There is no
assumed official registry image. Set `wikideck_image` to your actual tagged or assumed official registry image. Set `wikideck_image` to your actual tagged or
digest-pinned image. The role does not build/push images or install a cluster. preferably digest-pinned image. The role does not build/push images or install a cluster.
Rebuild from the current source: the image must include Node 24, libstdc++, and Rebuild from the current source: the image must include Node 24, libstdc++, and
the native RandomX executable, with `RANDOMX_BINARY` set by the image. An old the native RandomX executable, with `RANDOMX_BINARY` set by the image. An old
browser-mining image is incompatible. Build and verify for your worker node browser-mining image is incompatible. Build and verify for your worker node
architecture; do not assume a locally built image is multi-architecture. architecture; do not assume a locally built image is multi-architecture. The
production Dockerfile defaults to mainnet and pins its profile/genesis into the
image manifest; runtime settings cannot change that identity. Testnet is an
explicit development fixture, not a public deployment profile.
- For ingress: an ingress controller, DNS, and a trusted TLS Secret in the app - For ingress: an ingress controller, DNS, and a trusted TLS Secret in the app
namespace. Alternatively, configure cert-manager annotations and an existing namespace. Alternatively, configure cert-manager annotations and an existing
issuer. The role does not install cert-manager or create an issuer. issuer. The role does not install cert-manager or create an issuer.
@@ -43,30 +49,23 @@ environment. Windows controllers must run Ansible through WSL or Linux container
Install this checkout into your playbook's role search path as `roles/wikideck`, Install this checkout into your playbook's role search path as `roles/wikideck`,
or install it using an Ansible Galaxy Git role requirement named `wikideck`. or install it using an Ansible Galaxy Git role requirement named `wikideck`.
[examples/playbook.yml](examples/playbook.yml) shows an HTTPS deployment with [examples/playbook.yml](examples/playbook.yml) is the gated HTTPS deployment
ingress-nginx and cert-manager. Replace the image, hostname, peer URLs, class, example with ingress-nginx and cert-manager. It requires an approved image
issuer, and controller namespace with real values before running it. digest, the image's verified manifest/genesis, launch gates, and real peers before
it will change the cluster. There is intentionally no ungated, copy-and-run
```yaml mainnet snippet: use a pre-task gate like the example. Empty gates cause the
- name: Deploy a cluster-internal Wikideck instance application to exit before listening, but do not by themselves prevent Kubernetes
hosts: localhost resources from being created.
connection: local
gather_facts: false
roles:
- role: wikideck
vars:
wikideck_image: registry.example.com/games/wikideck:1.0.0
```
Run the playbook on the controller, using its normal kubeconfig discovery or Run the playbook on the controller, using its normal kubeconfig discovery or
`wikideck_kubeconfig` / `wikideck_kube_context` overrides. Do not target every `wikideck_kubeconfig` / `wikideck_kube_context` overrides. Do not target every
Kubernetes node: the role operates on the Kubernetes API, not over node SSH. Kubernetes node: the role operates on the Kubernetes API, not over node SSH.
For a private local preview, forward the Service with For an explicitly built testnet development image, forward the Service with
`kubectl -n wikideck port-forward service/wikideck 3001:80` and open `kubectl -n wikideck port-forward service/wikideck 3001:80` and open
<http://127.0.0.1:3001>. Keep the forwarding bind address at its loopback default. <http://127.0.0.1:3001>. Keep the forwarding bind address at its loopback default.
Wikideck already allows this local origin. Different hosts, ports, schemes, and The app allows this local origin. Different hosts, ports, schemes, and browser
browser profiles use different browser wallets. profiles use different browser wallets. This does not bypass mainnet startup gates.
For remote browsers, enable ingress and set `wikideck_public_origin` to exactly For remote browsers, enable ingress and set `wikideck_public_origin` to exactly
`https://` followed by `wikideck_ingress_host` (no slash or path). HTTPS is needed `https://` followed by `wikideck_ingress_host` (no slash or path). HTTPS is needed
@@ -81,6 +80,11 @@ Set `wikideck_peers` to a list of up to 16 compatible peer HTTPS origins, such a
An empty list deliberately starts an **isolated validator**. There is no automatic An empty list deliberately starts an **isolated validator**. There is no automatic
discovery, bootnode list, or automatic reciprocal configuration: configure the discovery, bootnode list, or automatic reciprocal configuration: configure the
other operators' nodes too. All nodes must run the same network/genesis/consensus. other operators' nodes too. All nodes must run the same network/genesis/consensus.
For mainnet, application startup additionally requires at least two distinct
public HTTPS peer origins, an arrived image-pinned genesis time, the exact chain-ID
approval, and explicit unaudited-risk acceptance. These switches are not launch
approval from this role; the project currently records a NO-GO decision. The role
does not discover or verify peer independence.
Peer RPC uses `/p2p/*` on the **same port 3001** as the UI/API; no separate TCP/UDP Peer RPC uses `/p2p/*` on the **same port 3001** as the UI/API; no separate TCP/UDP
P2P Service or host networking is required. The existing `/` ingress path forwards P2P Service or host networking is required. The existing `/` ingress path forwards
@@ -149,6 +153,8 @@ All defaults are in [defaults/main.yml](defaults/main.yml).
| `wikideck_image_pull_secrets` | `[]` | Names of existing registry Secrets in this namespace. | | `wikideck_image_pull_secrets` | `[]` | Names of existing registry Secrets in this namespace. |
| `wikideck_peers` | `[]` | Up to 16 static peer origins; empty means isolated. | | `wikideck_peers` | `[]` | Up to 16 static peer origins; empty means isolated. |
| `wikideck_allow_private_peers` | `false` | Explicit test-only private/HTTP peer permission. | | `wikideck_allow_private_peers` | `false` | Explicit test-only private/HTTP peer permission. |
| `wikideck_mainnet_launch` | empty | Exact 64-character chain-ID startup approval; leave empty unless a launch is formally approved. |
| `wikideck_mainnet_risk_ack` | empty | Must equal `operator-accepts-unaudited-risk` to acknowledge residual risk; not a certification. |
| `wikideck_public_origin` | empty | Additional allowed HTTPS browser origin; required with ingress. | | `wikideck_public_origin` | empty | Additional allowed HTTPS browser origin; required with ingress. |
| `wikideck_ingress_enabled` | `false` | Create TLS ingress; disabling removes the role's named ingress. | | `wikideck_ingress_enabled` | `false` | Create TLS ingress; disabling removes the role's named ingress. |
| `wikideck_ingress_host` | empty | DNS hostname, without scheme, port, or path. | | `wikideck_ingress_host` | empty | DNS hostname, without scheme, port, or path. |
@@ -161,6 +167,8 @@ All defaults are in [defaults/main.yml](defaults/main.yml).
| `wikideck_storage_access_modes` | `[ReadWriteOnce]` | `ReadWriteOnce` or `ReadWriteOncePod`; never `ReadWriteMany`. | | `wikideck_storage_access_modes` | `[ReadWriteOnce]` | `ReadWriteOnce` or `ReadWriteOncePod`; never `ReadWriteMany`. |
| `wikideck_resources` | see defaults | CPU/memory requests `250m`/`512Mi`, limits `1`/`1Gi`; small-validator starting point, not a capacity guarantee. | | `wikideck_resources` | see defaults | CPU/memory requests `250m`/`512Mi`, limits `1`/`1Gi`; small-validator starting point, not a capacity guarantee. |
| `wikideck_node_selector` / `wikideck_tolerations` / `wikideck_affinity` | `{}` / `[]` / `{}` | Pod placement settings. | | `wikideck_node_selector` / `wikideck_tolerations` / `wikideck_affinity` | `{}` / `[]` / `{}` | Pod placement settings. |
| `wikideck_trusted_proxies` | `[]` | Up to 16 literal proxy IPs/CIDRs; only trust actual edge source addresses after the edge overwrites forwarded headers. |
| `wikideck_ops_port` | empty | Optional port for a diagnostics listener bound only to container loopback; never add it to the Service, Ingress, or NetworkPolicy. |
| `wikideck_network_policy_enabled` | `true` | Manage network isolation; disabling deletes this role's policy. | | `wikideck_network_policy_enabled` | `true` | Manage network isolation; disabling deletes this role's policy. |
| `wikideck_network_policy_ingress_from` | same-namespace pods | List of NetworkPolicy peers allowed to reach TCP 3001; empty list denies all ingress. | | `wikideck_network_policy_ingress_from` | same-namespace pods | List of NetworkPolicy peers allowed to reach TCP 3001; empty list denies all ingress. |
| `wikideck_network_policy_dns_to` | `kube-system` namespace | DNS peers allowed on UDP/TCP 53; adapt for custom/NodeLocal DNS. | | `wikideck_network_policy_dns_to` | `kube-system` namespace | DNS peers allowed on UDP/TCP 53; adapt for custom/NodeLocal DNS. |
@@ -182,10 +190,14 @@ entrypoint are deliberately preserved; no production fake-PoW fallback is added.
`ReadWriteOncePod` if supported by your CSI driver. Do not force-delete an old `ReadWriteOncePod` if supported by your CSI driver. Do not force-delete an old
pod on a partitioned node until it has been fenced; Recreate is not a pod on a partitioned node until it has been fenced; Recreate is not a
distributed storage lock. distributed storage lock.
- `/app/data/testnet/ledger.json` stores the block DAG and pending transfers; - The active ledger is SQLite at `/app/data/<profile>/ledger.sqlite` (with WAL
`/app/data/testnet-node.lock` exclusively locks the entire data directory. sidecars); `/app/data/<profile>-node.lock` exclusively locks that profile's data.
The old demo chain is separate, not imported, and has **no migration** to the A matching legacy `ledger.json` may be imported once and is retained as a
new testnet. Back it up first; preferably use a fresh claim for the new network. recovery copy. The import path accepts at most 128 MiB and 10,000 blocks; these
are legacy-import limits, not live SQLite disk bounds. Different profiles and
chain identities must not share a writable claim. Preserve and back up existing
data before changing image profile or genesis; incompatible chain data fails
closed rather than resetting automatically.
The process runs The process runs
as UID/GID 1000 with `fsGroup: 1000`. Your storage driver must honor ownership as UID/GID 1000 with `fsGroup: 1000`. Your storage driver must honor ownership
settings, or the existing volume must already be writable by this identity. settings, or the existing volume must already be writable by this identity.
@@ -199,15 +211,18 @@ entrypoint are deliberately preserved; no production fake-PoW fallback is added.
backup or use your CSI provider's documented snapshot procedure. Encrypt disks backup or use your CSI provider's documented snapshot procedure. Encrypt disks
and backups, define retention, and test restores. Deleting a namespace or PVC and backups, define retention, and test restores. Deleting a namespace or PVC
can still destroy data depending on the underlying reclaim policy. can still destroy data depending on the underlying reclaim policy.
- The current `node:backup` utility creates/verifies/restores ledger-only packages
while the node is stopped. It does not contain browser wallets, secrets, or
configuration, and it does not encrypt the package; store it in restricted,
encrypted off-host storage and verify a restore before relying on it.
- Full native RandomX revalidation runs before the server listens. Increase - Full native RandomX revalidation runs before the server listens. Increase
`wikideck_startup_probe_failure_threshold` and `wikideck_wait_timeout` together `wikideck_startup_probe_failure_threshold` and `wikideck_wait_timeout` together
for your measured replay time, leaving scheduling/image-pull margin. A healthy for your measured replay time, leaving scheduling/image-pull margin. A healthy
empty node does not exercise the native cache or prove peer synchronization. empty node does not exercise the native cache or prove peer synchronization.
- The app bounds the ledger to 128 MiB and 10,000 total DAG blocks, with 256 - SQLite avoids whole-ledger JSON rewrites, but the node retains DAG records in
pending transfers. Complete NFT states are retained per DAG node, so RAM may memory, rebuilds state roots over the full NFT set, and revalidates proofs at
grow quadratically despite disk bounds. Whole-ledger rewrites and full replay startup. Long-chain capacity and archival-scale operation remain unresolved;
are **not suitable for a long-lived public chain**; a larger PVC does not fix a larger PVC does not fix memory, replay, or consensus-capacity limits.
those application limits.
### Crash recovery and resource sizing ### Crash recovery and resource sizing
@@ -268,12 +283,15 @@ your worker architecture and runtime security profile; HTTP health alone cannot
detect every native engine failure. Do not grant privileged mode or disable detect every native engine failure. Do not grant privileged mode or disable
seccomp as an untested performance workaround. seccomp as an untested performance workaround.
Wikideck does not currently configure Express `trust proxy`. Clients behind one `WIKIDECK_TRUSTED_PROXIES` is empty/untrusted by default. Populate
proxy can therefore share an IP-based application rate limit. Review the `wikideck_trusted_proxies` only with the literal source addresses/CIDRs actually
application before public exposure; do not try to fix this by blindly trusting used by the trusted edge; the app validates them at startup. The edge must
forwarded headers. The current app limits `/api` and `/p2p` together; default overwrite forwarded headers, and network controls must prevent direct backend
forwarded-header behavior can also produce express-rate-limit validation errors access. Do not copy a generic proxy range without verifying the cluster path.
in logs. The role adds no authentication, WAF, or abuse protection. Eclipse, The optional `WIKIDECK_OPS_PORT` listener is loopback-only inside the pod and is
not exposed by role resources. It is intended for local/container-exec checks,
not an ingress endpoint. The role adds no authentication, WAF, or abuse
protection. Eclipse,
majority-hashpower, spam/DoS and reorganization risks remain application/protocol majority-hashpower, spam/DoS and reorganization risks remain application/protocol
issues; deployment hardening does not make a public mainnet safe. issues; deployment hardening does not make a public mainnet safe.
@@ -282,8 +300,8 @@ issues; deployment hardening does not make a public mainnet safe.
[tests/render.yml](tests/render.yml) runs entirely offline: it validates inputs, [tests/render.yml](tests/render.yml) runs entirely offline: it validates inputs,
renders/parses every template across default, ingress, existing-claim, storage renders/parses every template across default, ingress, existing-claim, storage
class, isolated/public/private peers, custom HTTPS ports, deny-all, longer startup, class, isolated/public/private peers, custom HTTPS ports, deny-all, longer startup,
and digest-image variants, and checks wiring/security digest-image, launch-gate, proxy-trust and diagnostics variants, and checks
invariants. It also checks that unsafe/invalid settings are rejected. Run it with wiring/security invariants. It also checks that unsafe/invalid settings are rejected. Run it with
`ansible-playbook -i tests/inventory tests/render.yml`. `ansible-playbook -i tests/inventory tests/render.yml`.
[tests/test.yml](tests/test.yml) is a real-cluster smoke test using the local [tests/test.yml](tests/test.yml) is a real-cluster smoke test using the local
+7
View File
@@ -21,6 +21,9 @@ wikideck_image_pull_secrets: []
wikideck_peers: [] wikideck_peers: []
# Test/private networks only; permits private addresses AND HTTP in the app. # Test/private networks only; permits private addresses AND HTTP in the app.
wikideck_allow_private_peers: false wikideck_allow_private_peers: false
# Explicit mainnet candidate startup gates. Never set these before launch approval.
wikideck_mainnet_launch: ""
wikideck_mainnet_risk_ack: ""
# Exact browser origin, without a trailing slash. Required when using ingress. # Exact browser origin, without a trailing slash. Required when using ingress.
wikideck_public_origin: "" wikideck_public_origin: ""
wikideck_ingress_enabled: false wikideck_ingress_enabled: false
@@ -48,6 +51,10 @@ wikideck_resources:
wikideck_node_selector: {} wikideck_node_selector: {}
wikideck_tolerations: [] wikideck_tolerations: []
wikideck_affinity: {} wikideck_affinity: {}
# Trust only literal proxy source IPs/CIDRs whose edge overwrites forwarded headers.
wikideck_trusted_proxies: []
# Optional private operations listener; application binds it to container loopback.
wikideck_ops_port: ""
# Requires a NetworkPolicy-capable CNI. Same-namespace clients only by default. # Requires a NetworkPolicy-capable CNI. Same-namespace clients only by default.
wikideck_network_policy_enabled: true wikideck_network_policy_enabled: true
+23 -7
View File
@@ -2,16 +2,32 @@
- name: Deploy Wikideck - name: Deploy Wikideck
hosts: localhost hosts: localhost
connection: local connection: local
gather_facts: false gather_facts: true
pre_tasks:
- name: Require an approved and ready mainnet candidate release
ansible.builtin.assert:
that:
- wikideck_mainnet_deployment_approved | default(false) | bool
- wikideck_release_image is defined and wikideck_release_image is string and wikideck_release_image is match('^.+@sha256:[a-f0-9]{64}$')
- wikideck_release_peers is defined and wikideck_release_peers is sequence and wikideck_release_peers is not string and wikideck_release_peers | length >= 2
- wikideck_release_peers | unique | list | length == wikideck_release_peers | length
- wikideck_candidate_chain_id is defined and wikideck_candidate_chain_id is match('^[a-f0-9]{64}$')
- wikideck_candidate_risk_ack | default('') == 'operator-accepts-unaudited-risk'
- wikideck_release_genesis_time is defined and (wikideck_release_genesis_time | string) is match('^[1-9][0-9]{8,10}$')
- wikideck_release_genesis_time | int >= 1700000000 and wikideck_release_genesis_time | int <= 4102444800
- wikideck_release_genesis_time | int <= ansible_date_time.epoch | int
fail_msg: >-
Mainnet is currently NO-GO. Supply only an approved digest-pinned image,
verified frozen manifest and arrived genesis, independent public HTTPS
peers, and explicit unaudited-risk acceptance.
roles: roles:
- role: wikideck - role: wikideck
vars: vars:
# Replace with your published, scanned image; preferably use @sha256:... wikideck_image: "{{ wikideck_release_image }}"
wikideck_image: registry.example.com/games/wikideck:1.0.0 wikideck_image_pull_policy: IfNotPresent
# Replace with real compatible testnet peers. No bootnodes are supplied. wikideck_peers: "{{ wikideck_release_peers }}"
wikideck_peers: wikideck_mainnet_launch: "{{ wikideck_candidate_chain_id }}"
- https://peer-a.example.org wikideck_mainnet_risk_ack: "{{ wikideck_candidate_risk_ack }}"
- https://peer-b.example.org
wikideck_ingress_enabled: true wikideck_ingress_enabled: true
wikideck_ingress_host: wikideck.example.com wikideck_ingress_host: wikideck.example.com
wikideck_public_origin: https://wikideck.example.com wikideck_public_origin: https://wikideck.example.com
+1 -1
View File
@@ -3,7 +3,7 @@
galaxy_info: galaxy_info:
role_name: wikideck role_name: wikideck
author: Eric Meehan author: Eric Meehan
description: Deploy Wikideck to Kubernetes with persistent storage and optional TLS ingress. description: Deploy the gated Wikideck mainnet candidate or an explicit development node to Kubernetes.
license: MIT-0 license: MIT-0
min_ansible_version: "2.16" min_ansible_version: "2.16"
galaxy_tags: galaxy_tags:
+45
View File
@@ -0,0 +1,45 @@
- name: Deploy Wikideck
hosts: alpha-control-plane
become: true
vars_files:
- ~/Desktop/secrets.yaml
pre_tasks:
- name: Require an explicitly approved, immutable mainnet release
ansible.builtin.assert:
that:
- wikideck_mainnet_deployment_approved | default(false) | bool
- wikideck_release_image is defined and wikideck_release_image is string and wikideck_release_image is match('^.+@sha256:[a-f0-9]{64}$')
- wikideck_mainnet_peers is defined and wikideck_mainnet_peers is sequence and wikideck_mainnet_peers is not string and wikideck_mainnet_peers | length >= 2
- wikideck_mainnet_peers | unique | list | length == wikideck_mainnet_peers | length
- wikideck_candidate_chain_id is defined and wikideck_candidate_chain_id is match('^[a-f0-9]{64}$')
- wikideck_candidate_risk_ack | default('') == 'operator-accepts-unaudited-risk'
- wikideck_release_genesis_time is defined and (wikideck_release_genesis_time | string) is match('^[1-9][0-9]{8,10}$')
- wikideck_release_genesis_time | int >= 1700000000 and wikideck_release_genesis_time | int <= 4102444800
- wikideck_release_genesis_time | int <= ansible_date_time.epoch | int
fail_msg: >-
Mainnet is currently NO-GO. Do not deploy until the operator has approved
a digest-pinned image with a verified frozen manifest, arrived genesis,
at least two independent public HTTPS peers, and explicit risk acceptance.
roles:
- role: ericomeehan.wikideck
vars:
wikideck_image: "{{ wikideck_release_image }}"
wikideck_image_pull_policy: IfNotPresent
wikideck_peers: "{{ wikideck_mainnet_peers }}"
wikideck_mainnet_launch: "{{ wikideck_candidate_chain_id }}"
wikideck_mainnet_risk_ack: "{{ wikideck_candidate_risk_ack }}"
wikideck_public_origin: https://wikideck.eom.dev
wikideck_ingress_enabled: true
wikideck_ingress_host: wikideck.eom.dev
wikideck_ingress_class_name: nginx
wikideck_ingress_annotations:
cert-manager.io/cluster-issuer: ca-issuer
# Match the actual ingress controller namespace; add its podSelector if known.
wikideck_network_policy_ingress_from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
wikideck_storage_size: 2Ti
wikideck_storage_class: nfs-client
# Populate only after confirming the real proxy source IPs/CIDRs.
wikideck_trusted_proxies: "{{ wikideck_ingress_proxy_sources | default([]) }}"
+11
View File
@@ -28,6 +28,16 @@
- wikideck_existing_claim | length == 0 or wikideck_existing_claim is match('^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$') - wikideck_existing_claim | length == 0 or wikideck_existing_claim is match('^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$')
- wikideck_public_origin is string - wikideck_public_origin is string
- wikideck_public_origin | length == 0 or wikideck_public_origin is match('^https://[a-z0-9.-]+(:[0-9]+)?$') - wikideck_public_origin | length == 0 or wikideck_public_origin is match('^https://[a-z0-9.-]+(:[0-9]+)?$')
- wikideck_mainnet_launch is string
- wikideck_mainnet_launch | length == 0 or wikideck_mainnet_launch is match('^[a-f0-9]{64}$')
- wikideck_mainnet_risk_ack is string
- wikideck_mainnet_risk_ack in ['', 'operator-accepts-unaudited-risk']
- wikideck_trusted_proxies is sequence and wikideck_trusted_proxies is not string and wikideck_trusted_proxies is not mapping
- wikideck_trusted_proxies | length <= 16
- wikideck_trusted_proxies | select('string') | list | length == wikideck_trusted_proxies | length
- wikideck_trusted_proxies | select('match', '^[0-9a-fA-F:.]+(/[0-9]{1,3})?$') | list | length == wikideck_trusted_proxies | length
- wikideck_ops_port is string or wikideck_ops_port is integer
- wikideck_ops_port | string | length == 0 or ((wikideck_ops_port | string) is match('^[1-9][0-9]{0,4}$') and wikideck_ops_port | int <= 65535)
- wikideck_ingress_enabled is boolean - wikideck_ingress_enabled is boolean
- wikideck_network_policy_enabled is boolean - wikideck_network_policy_enabled is boolean
- wikideck_allow_private_peers is boolean - wikideck_allow_private_peers is boolean
@@ -68,6 +78,7 @@
- wikideck_network_policy_https_ports - wikideck_network_policy_https_ports
- wikideck_network_policy_peer_egress - wikideck_network_policy_peer_egress
- wikideck_peers - wikideck_peers
- wikideck_trusted_proxies
- name: Validate the static peer allowlist - name: Validate the static peer allowlist
ansible.builtin.assert: ansible.builtin.assert:
+12
View File
@@ -71,6 +71,18 @@ spec:
value: {{ (wikideck_peers | join(',')) | to_json }} value: {{ (wikideck_peers | join(',')) | to_json }}
- name: WIKIDECK_ALLOW_PRIVATE_PEERS - name: WIKIDECK_ALLOW_PRIVATE_PEERS
value: {{ ('1' if wikideck_allow_private_peers else '0') | to_json }} value: {{ ('1' if wikideck_allow_private_peers else '0') | to_json }}
- name: WIKIDECK_MAINNET_LAUNCH
value: {{ wikideck_mainnet_launch | to_json }}
- name: WIKIDECK_MAINNET_RISK_ACK
value: {{ wikideck_mainnet_risk_ack | to_json }}
{% if wikideck_trusted_proxies | length > 0 %}
- name: WIKIDECK_TRUSTED_PROXIES
value: {{ (wikideck_trusted_proxies | join(',')) | to_json }}
{% endif %}
{% if wikideck_ops_port | string | length > 0 %}
- name: WIKIDECK_OPS_PORT
value: {{ (wikideck_ops_port | string) | to_json }}
{% endif %}
{% if wikideck_public_origin | length > 0 %} {% if wikideck_public_origin | length > 0 %}
- name: PUBLIC_ORIGIN - name: PUBLIC_ORIGIN
value: {{ wikideck_public_origin | to_json }} value: {{ wikideck_public_origin | to_json }}
+4
View File
@@ -19,6 +19,10 @@
wikideck_network_policy_peer_egress: "{{ invalid_case.peer_egress | default([]) }}" wikideck_network_policy_peer_egress: "{{ invalid_case.peer_egress | default([]) }}"
wikideck_image_pull_secrets: "{{ invalid_case.pull_secrets | default([]) }}" wikideck_image_pull_secrets: "{{ invalid_case.pull_secrets | default([]) }}"
wikideck_network_policy_ingress_from: "{{ invalid_case.ingress_from | default([{'podSelector': {}}]) }}" wikideck_network_policy_ingress_from: "{{ invalid_case.ingress_from | default([{'podSelector': {}}]) }}"
wikideck_trusted_proxies: "{{ invalid_case.trusted_proxies | default([]) }}"
wikideck_ops_port: "{{ invalid_case.ops_port | default('') }}"
wikideck_mainnet_launch: "{{ invalid_case.mainnet_launch | default('') }}"
wikideck_mainnet_risk_ack: "{{ invalid_case.risk_ack | default('') }}"
block: block:
- name: Run role input validation - name: Run role input validation
ansible.builtin.include_tasks: ../tasks/validate.yml ansible.builtin.include_tasks: ../tasks/validate.yml
+19
View File
@@ -15,6 +15,10 @@
wikideck_namespace: "{{ render_case.namespace | default('wikideck') }}" wikideck_namespace: "{{ render_case.namespace | default('wikideck') }}"
wikideck_peers: "{{ render_case.peers | default([]) }}" wikideck_peers: "{{ render_case.peers | default([]) }}"
wikideck_allow_private_peers: "{{ render_case.allow_private | default(false) }}" wikideck_allow_private_peers: "{{ render_case.allow_private | default(false) }}"
wikideck_mainnet_launch: "{{ render_case.mainnet_launch | default('') }}"
wikideck_mainnet_risk_ack: "{{ render_case.risk_ack | default('') }}"
wikideck_trusted_proxies: "{{ render_case.trusted_proxies | default([]) }}"
wikideck_ops_port: "{{ render_case.ops_port | default('') }}"
wikideck_startup_probe_failure_threshold: "{{ render_case.startup_threshold | default(180) }}" wikideck_startup_probe_failure_threshold: "{{ render_case.startup_threshold | default(180) }}"
wikideck_ingress_enabled: "{{ render_case.ingress | default(false) }}" wikideck_ingress_enabled: "{{ render_case.ingress | default(false) }}"
wikideck_ingress_host: wikideck.example.com wikideck_ingress_host: wikideck.example.com
@@ -73,6 +77,13 @@
https_to: [] https_to: []
- label: digest-pinned image - label: digest-pinned image
image: registry.example.com/wikideck@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa image: registry.example.com/wikideck@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- label: proxy trust and private diagnostics
peers: [https://peer-a.example.org, https://peer-b.example.org]
trusted_proxies: [10.20.30.40, 2001:db8::/64]
ops_port: "9090"
- label: exact mainnet launch configuration
mainnet_launch: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
risk_ack: operator-accepts-unaudited-risk
loop_control: loop_control:
loop_var: render_case loop_var: render_case
label: "{{ render_case.label }}" label: "{{ render_case.label }}"
@@ -146,6 +157,14 @@
peer_egress: peer_egress:
- to: [{podSelector: {}}] - to: [{podSelector: {}}]
ports: [{protocol: UDP, port: 3001}] ports: [{protocol: UDP, port: 3001}]
- label: malformed mainnet chain approval
mainnet_launch: not-a-chain-id
- label: unsupported mainnet risk acknowledgement
risk_ack: operator-accepts-risk
- label: proxy entry is not an IP or CIDR
trusted_proxies: [proxy.example.org]
- label: operations port out of range
ops_port: "70000"
loop_control: loop_control:
loop_var: invalid_case loop_var: invalid_case
label: "{{ invalid_case.label }}" label: "{{ invalid_case.label }}"
+4
View File
@@ -48,6 +48,10 @@
- env.WIKIDECK_DATA_DIR == '/app/data' and env.NODE_ENV == 'production' - env.WIKIDECK_DATA_DIR == '/app/data' and env.NODE_ENV == 'production'
- env.WIKIDECK_PEERS == wikideck_peers | join(',') - env.WIKIDECK_PEERS == wikideck_peers | join(',')
- env.WIKIDECK_ALLOW_PRIVATE_PEERS == ('1' if wikideck_allow_private_peers else '0') - env.WIKIDECK_ALLOW_PRIVATE_PEERS == ('1' if wikideck_allow_private_peers else '0')
- env.WIKIDECK_MAINNET_LAUNCH == wikideck_mainnet_launch
- env.WIKIDECK_MAINNET_RISK_ACK == wikideck_mainnet_risk_ack
- (env.WIKIDECK_TRUSTED_PROXIES | default('')) == wikideck_trusted_proxies | join(',')
- (env.WIKIDECK_OPS_PORT | default('')) == (wikideck_ops_port | string)
- env.WIKIDECK_OFFLINE is not defined - env.WIKIDECK_OFFLINE is not defined
- env.RANDOMX_BINARY is not defined - env.RANDOMX_BINARY is not defined
- container.command is not defined and container.args is not defined - container.command is not defined and container.args is not defined
+13 -6
View File
@@ -2,7 +2,8 @@
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import { spawn, execFileSync } from 'node:child_process'; import { spawn, execFileSync } from 'node:child_process';
import { once } from 'node:events'; import { once } from 'node:events';
import { readFile, access } from 'node:fs/promises'; import { access } from 'node:fs/promises';
import { DatabaseSync } from 'node:sqlite';
assert.equal(process.env.WIKIDECK_DATA_DIR, '/app/data'); assert.equal(process.env.WIKIDECK_DATA_DIR, '/app/data');
assert.equal(process.env.WIKIDECK_PEERS, ''); assert.equal(process.env.WIKIDECK_PEERS, '');
@@ -39,17 +40,23 @@ async function runNode() {
const body = await response.json(); const body = await response.json();
if (path === '/healthz') { if (path === '/healthz') {
assert.equal(body.status, 'ok'); assert.equal(body.status, 'ok');
assert.equal(body.network, 'wikideck-randomx-testnet-2'); assert.equal(body.network, 'wikideck-randomx-testnet-6');
} else if (path === '/p2p/status') { } else if (path === '/p2p/status') {
assert.equal(body.network, 'wikideck-randomx-testnet-2'); assert.equal(body.network, 'wikideck-randomx-testnet-6');
assert.equal(body.size, 0); assert.equal(body.size, 0);
assert.match(body.chainId, /^[a-f0-9]{64}$/); assert.match(body.chainId, /^[a-f0-9]{64}$/);
} else assert.deepEqual(body.peers, []); } else assert.deepEqual(body.peers, []);
} }
await access('/app/data/testnet-node.lock'); await access('/app/data/testnet-node.lock');
const ledger = JSON.parse(await readFile('/app/data/testnet/ledger.json', 'utf8')); const ledger = new DatabaseSync('/app/data/testnet/ledger.sqlite');
assert.equal(ledger.network, 'wikideck-randomx-testnet-2'); try {
assert.deepEqual(ledger.blocks, []); const metadata = ledger.prepare('SELECT value FROM metadata WHERE key = ?').get('network');
assert.equal(metadata.value, 'wikideck-randomx-testnet-6');
assert.equal(ledger.prepare('SELECT COUNT(*) AS count FROM blocks').get().count, 0);
assert.equal(ledger.prepare('SELECT COUNT(*) AS count FROM pending_transfers').get().count, 0);
} finally {
ledger.close();
}
child.kill('SIGTERM'); child.kill('SIGTERM');
assert.deepEqual(await exited, [0, null], output); assert.deepEqual(await exited, [0, null], output);
await assert.rejects(access('/app/data/testnet-node.lock'), { code: 'ENOENT' }); await assert.rejects(access('/app/data/testnet-node.lock'), { code: 'ENOENT' });