initial commit
All checks were successful
Build & publish Docker images / Build & push all images (push) Successful in 2s
All checks were successful
Build & publish Docker images / Build & push all images (push) Successful in 2s
This commit is contained in:
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
* text=auto
|
||||||
|
Dockerfile text eol=lf
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.py text eol=lf
|
||||||
43
.gitea/workflows/docker.yml
Normal file
43
.gitea/workflows/docker.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Build & publish Docker images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.hexadual.io
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build & push all images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 \
|
||||||
|
https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@git.hexadual.io/${{ github.repository }}.git \
|
||||||
|
.
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.REGISTRY_TOKEN }}" | \
|
||||||
|
docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
|
||||||
|
|
||||||
|
- name: Build & push egg
|
||||||
|
run: |
|
||||||
|
docker compose build egg
|
||||||
|
docker compose push egg
|
||||||
|
docker tag ${{ env.REGISTRY }}/rocobo/gcp-dot-egg:latest \
|
||||||
|
${{ env.REGISTRY }}/rocobo/gcp-dot-egg:${{ github.sha }}
|
||||||
|
docker push ${{ env.REGISTRY }}/rocobo/gcp-dot-egg:${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Logout
|
||||||
|
if: always()
|
||||||
|
run: docker logout ${{ env.REGISTRY }} || true
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.env
|
||||||
|
*.db
|
||||||
|
.DS_Store
|
||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.14-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY egg.py .
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
CMD ["python", "egg.py"]
|
||||||
84
README.md
Normal file
84
README.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# GCP Egg
|
||||||
|
|
||||||
|
Distributed client for the **Global Consciousness Project** — a self-hosted network variance tracker inspired by the [Global Consciousness Project](https://global-mind.org).
|
||||||
|
|
||||||
|
Each "egg" container draws 200 random bits per second from the OS hardware-entropy pool and submits the count of 1-bits to a central server, which runs Stouffer Z-score analysis and displays a live colored dot.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Egg containers (anyone can run)
|
||||||
|
│ POST /api/data (one 200-bit trial per second)
|
||||||
|
▼
|
||||||
|
Server (FastAPI + SQLite)
|
||||||
|
│ Stouffer Z network variance analysis every 60 s
|
||||||
|
▼
|
||||||
|
Website → animated dot + history chart + embeddable iframe
|
||||||
|
```
|
||||||
|
|
||||||
|
The server is maintained separately. This repo contains only the egg client.
|
||||||
|
|
||||||
|
## Run an Egg
|
||||||
|
|
||||||
|
Download the compose file and start it — no building required:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -O https://git.hexadual.io/rocobo/GCP-Dot/raw/branch/main/docker-compose.yml
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker will pull the pre-built image from the registry automatically. The egg will immediately start sending one trial per second and persist its ID across restarts.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `SERVER_URL` | `https://gcp.hexadual.io` | Server to send trials to |
|
||||||
|
| `EGG_ID` | auto-generated | Override the egg's unique identifier |
|
||||||
|
|
||||||
|
The auto-generated ID is derived from a SHA-256 hash stored at `/data/egg_id` — mount a volume there to keep it stable across restarts.
|
||||||
|
|
||||||
|
## Published Image
|
||||||
|
|
||||||
|
The egg image is built and published automatically on every push to `main`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull git.hexadual.io/rocobo/gcp-dot-egg:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## How the Analysis Works
|
||||||
|
|
||||||
|
The server analyses the past hour of data every 60 seconds:
|
||||||
|
|
||||||
|
1. **Normalise** each trial to a Z-score: `z = (trial − 100) / √50`
|
||||||
|
(Binomial(200, 0.5) has mean = 100, variance = 50)
|
||||||
|
2. **Stouffer Z** per second across all active eggs: `S_t = Σzᵢ / √N`
|
||||||
|
3. **Network variance**: `V = Σ S_t²` — follows χ²(T) under H₀
|
||||||
|
4. **Index** = lower-tail CDF × 100
|
||||||
|
|
||||||
|
### Color Table
|
||||||
|
|
||||||
|
| Color | Index | Meaning |
|
||||||
|
|---|---|---|
|
||||||
|
| Blue | > 95% | Significantly small variance — deep coherence |
|
||||||
|
| Cyan | 90–95% | Small variance — probable coherence |
|
||||||
|
| Green | 40–90% | Normal random behavior |
|
||||||
|
| Yellow | 10–40% | Slightly elevated variance |
|
||||||
|
| Orange | 5–10% | Strongly elevated variance |
|
||||||
|
| Red | < 5% | Significantly large variance |
|
||||||
|
|
||||||
|
## Embed the Dot
|
||||||
|
|
||||||
|
```html
|
||||||
|
<iframe src="https://gcp.hexadual.io/gcp.html"
|
||||||
|
height="48" width="48" scrolling="no" frameborder="0"></iframe>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server API Reference
|
||||||
|
|
||||||
|
| Endpoint | Description |
|
||||||
|
|---|---|
|
||||||
|
| `POST /api/data` | Submit a trial `{egg_id, timestamp, trial}` |
|
||||||
|
| `GET /api/status` | Latest analysis result |
|
||||||
|
| `GET /api/history?limit=60` | Last N analysis records |
|
||||||
|
| `GET /api/eggs` | Eggs active in the last 2 minutes |
|
||||||
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
egg:
|
||||||
|
image: git.hexadual.io/rocobo/gcp-dot-egg:latest
|
||||||
|
volumes:
|
||||||
|
- gcp_egg_data:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- SERVER_URL=https://gcp.hexadual.io
|
||||||
|
- PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
gcp_egg_data:
|
||||||
71
egg.py
Normal file
71
egg.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import hashlib
|
||||||
|
import platform
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
ID_FILE = "/data/egg_id"
|
||||||
|
|
||||||
|
|
||||||
|
def generate_trial():
|
||||||
|
"""Sum of 200 random bits drawn from the OS CSPRNG (hardware entropy pool)."""
|
||||||
|
raw = os.urandom(25) # 25 bytes = 200 bits
|
||||||
|
return sum(bin(b).count("1") for b in raw)
|
||||||
|
|
||||||
|
|
||||||
|
def load_or_create_id():
|
||||||
|
try:
|
||||||
|
with open(ID_FILE) as f:
|
||||||
|
egg_id = f.read().strip()
|
||||||
|
if egg_id:
|
||||||
|
return egg_id
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
egg_id = hashlib.sha256(os.urandom(32)).hexdigest()[:16]
|
||||||
|
try:
|
||||||
|
os.makedirs("/data", exist_ok=True)
|
||||||
|
with open(ID_FILE, "w") as f:
|
||||||
|
f.write(egg_id)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return egg_id
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
server_url = os.environ.get("SERVER_URL", "http://localhost:8000").rstrip("/")
|
||||||
|
egg_id = os.environ.get("EGG_ID") or load_or_create_id()
|
||||||
|
|
||||||
|
print(f"GCP Egg id={egg_id} server={server_url} platform={platform.system()}")
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
errors = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
loop_start = time.time()
|
||||||
|
timestamp = int(loop_start)
|
||||||
|
trial = generate_trial()
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = session.post(
|
||||||
|
f"{server_url}/api/data",
|
||||||
|
json={"egg_id": egg_id, "timestamp": timestamp, "trial": trial},
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
errors = 0
|
||||||
|
else:
|
||||||
|
errors += 1
|
||||||
|
if errors % 10 == 1:
|
||||||
|
print(f"Server returned {resp.status_code} (error #{errors})")
|
||||||
|
except Exception as exc:
|
||||||
|
errors += 1
|
||||||
|
if errors % 10 == 1:
|
||||||
|
print(f"Send error (#{errors}): {exc}")
|
||||||
|
|
||||||
|
elapsed = time.time() - loop_start
|
||||||
|
time.sleep(max(0.0, 1.0 - elapsed))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
requests>=2.32.0
|
||||||
Reference in New Issue
Block a user