Compare commits

...

14 Commits

Author SHA1 Message Date
Jeff Emmett c4a2bc7164 Fix redacted placeholders in public-facing files
Restore contact email in website (public-facing).
Use env var reference for workflow notification address.
Clean up backlog task notes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 01:03:33 +00:00
Jeff Emmett 19cf1de886 Move all SMTP config to env vars, no hardcoded values
SMTP host, port, and sender address are now sourced from
.env like all other credentials. No service-identifying
details remain in tracked files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 01:02:39 +00:00
Jeff Emmett 2ead9ed666 Remove hardcoded DB password default from docker-compose
All secrets now come from .env file on the server.
No default/fallback values for any credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 00:50:04 +00:00
Jeff Emmett 7781dad704 Switch n8n email from Resend API to Mailcow SMTP
Replace all Resend HTTP API calls with n8n built-in emailSend
nodes using Mailcow SMTP at mx.jeffemmett.com:587 (STARTTLS).
Add SMTP env vars and n8n API key to docker-compose config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 00:30:05 +00:00
Jeff Emmett 746ae71601 docs(backlog): initialize backlog and add infrastructure task (task-1)
Set up backlog tracking for CosmoLocal. Task-1 documents completed
infrastructure: Mailcow SMTP, Docmost workspace, Listmonk newsletter
list with per-list RBAC for Bryan, and email DNS authentication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 00:20:52 +00:00
Jeff Emmett 4e777970cd Remove hardcoded API keys and credentials from tracked files
Move RESEND_API_KEY and Listmonk credentials to .env file
on the server. No secrets should be committed to the repo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:19:40 +00:00
Jeff Emmett 67bb61888b Add API keys as Docker env vars for n8n community edition
n8n community edition doesn't have Settings > Variables.
Pass TWENTY_API_KEY and RESEND_API_KEY as container
environment variables instead, accessible via $env.* in workflows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 15:35:20 +00:00
Jeff Emmett 5b87103fdb Add 5 n8n CRM automation workflows
- 01: Contact intake webhook → Twenty CRM
- 02: Lead nurturing 3-email sequence via Resend
- 03: Daily CRM → Listmonk newsletter sync
- 04: Weekly stale contact follow-up reminders
- 05: Gitea/GitHub webhook events → CRM activity log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 14:20:28 +00:00
Jeff Emmett acd874924f Add n8n instance at automate.cosmolocal.world
Adds n8n workflow automation with PostgreSQL backend,
Traefik routing for automate.cosmolocal.world, and
isolated internal network for the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 13:56:25 +00:00
Jeff Emmett 93fcbd45ef Restyle hero with gradient background, remove old tagline
Replace plain hero with rich forest-green gradient, radial glow
effects, and subtle grid pattern. Remove "Bridging cosmic
perspective with local action" tagline. Hero heading is now
just "Cosmolocal Foundation" with white-on-dark treatment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 13:50:36 +00:00
Jeff Emmett a36244187a Add deployment scaffolding (Dockerfile, docker-compose, nginx)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 14:14:29 +01:00
Jeff Emmett 0cf11b32fb Add comprehensive content from foundation documentation
Expand site from 4 to 9 sections with vision, mission pillars,
8 strategic priorities, founder bio (Michel Bauwens), investment
case with Cosmo-Local Financing Facility, core values from
Social Charter, and governance framework. Sticky nav with
backdrop blur, updated metadata/OpenGraph.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 13:02:06 +00:00
Jeff Emmett 810d6bbf33 Add public dir placeholder for Docker build
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:28:58 +00:00
Jeff Emmett fc75b92a29 Initial Cosmolocal Foundation website
Next.js 16 + Tailwind CSS landing page with hero, about, mission,
and contact sections. Dockerized for Netcup deployment with Traefik.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:27:33 +00:00
21 changed files with 2117 additions and 62 deletions

7
.dockerignore Normal file
View File

@ -0,0 +1,7 @@
node_modules
.next
.git
.gitignore
Dockerfile
docker-compose.yml
*.md

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
FROM node:22-alpine AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

5
backlog/config.yml Normal file
View File

@ -0,0 +1,5 @@
project_name: CosmoLocal Website
project_id: cosmolocal-website
description: CosmoLocal World website and related infrastructure
created: '2026-02-09'
integration_mode: mcp

View File

@ -0,0 +1,65 @@
---
id: task-1
title: Set up CosmoLocal email, docs, and newsletter infrastructure
status: Done
assignee: ['@claude']
created_date: '2026-02-09 12:00'
updated_date: '2026-02-09 21:30'
labels: [infrastructure, email, docs, newsletter]
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Set up CosmoLocal World infrastructure: Mailcow SMTP for cosmolocal.world, Docmost workspace at docs.cosmolocal.world, Listmonk newsletter list with per-list RBAC for Bryan, and email authentication (DKIM, SPF, DMARC).
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Configure Mailcow SMTP for cosmolocal.world domain
- [x] #2 Set up DNS records (SPF, DKIM, DMARC) for cosmolocal.world
- [x] #3 Create noreply@cosmolocal.world mailbox with newsletter@ alias
- [x] #4 Deploy Docmost at docs.cosmolocal.world (separate workspace, shared infra)
- [x] #5 Configure SMTP for Docmost CosmoLocal instance
- [x] #6 Create CosmoLocal World list in Listmonk
- [x] #7 Set up Bryan as editor with per-list RBAC (CosmoLocal list only)
- [x] #8 Set up Google Postmaster Tools for cosmolocal.world
- [x] #9 Configure Traefik websecure + Let's Encrypt for docs.cosmolocal.world
- [x] #10 Invite Bryan to CosmoLocal Docmost workspace
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
### Mailcow (cosmolocal.world)
- Mailbox: noreply@cosmolocal.world
- Alias: newsletter@cosmolocal.world → noreply@cosmolocal.world (sender_allowed=1)
- SMTP: [SMTP_HOST]:465 (TLS)
- DNS: SPF, DKIM (2048-bit), DMARC all configured on Cloudflare
- Google Postmaster Tools verified
### Docmost (docs.cosmolocal.world)
- Separate Docmost app container (docmost-cl) sharing Postgres + Redis with docs.jeffemmett.com
- Database: docmost_cosmolocal (in shared docmost-db Postgres)
- Redis: db 1 (shared docmost-redis)
- DNS: proxied A record → 159.195.32.209
- SSL: Traefik websecure entrypoint + Let's Encrypt cert
- SMTP: noreply@cosmolocal.world via Mailcow
- Location: /opt/apps/docmost/docker-compose.yml (single compose file)
### Listmonk (newsletter.cosmolocal.world)
- CosmoLocal World list created (id=21, public, single opt-in)
- SMTP server "cosmolocal.world" configured in Listmonk settings
- Bryan's account: bryan / CosmoLocal-e2dc5eec
- User role: Editor (campaigns, subscribers, templates, media - no admin)
- List role: CosmoLocal Editor (scoped to CosmoLocal World list only)
- Cannot see other lists, settings, users, or roles
### Bryan's Access Summary
| Service | URL | Username | Role |
|---------|-----|----------|------|
| Listmonk | newsletter.jeffemmett.com/admin | bryan | Editor (CosmoLocal list only) |
| Docmost | docs.cosmolocal.world | bryan@cosmolocal.world | Member (invited) |
<!-- SECTION:NOTES:END -->

86
docker-compose.yml Normal file
View File

@ -0,0 +1,86 @@
services:
cosmolocal-website:
build: .
container_name: cosmolocal-website
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.cosmolocal.rule=Host(`cosmolocal.world`) || Host(`www.cosmolocal.world`)"
- "traefik.http.routers.cosmolocal.entrypoints=web"
- "traefik.http.services.cosmolocal.loadbalancer.server.port=3000"
- "traefik.http.routers.cosmolocal-secure.rule=Host(`cosmolocal.world`) || Host(`www.cosmolocal.world`)"
- "traefik.http.routers.cosmolocal-secure.entrypoints=websecure"
- "traefik.http.routers.cosmolocal-secure.tls=true"
- "traefik.http.routers.cosmolocal-secure.service=cosmolocal"
networks:
- traefik-public
n8n-cosmolocal:
image: n8nio/n8n:latest
container_name: n8n-cosmolocal
restart: unless-stopped
environment:
- N8N_HOST=automate.cosmolocal.world
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://automate.cosmolocal.world/
- GENERIC_TIMEZONE=Europe/Brussels
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=n8n-cosmolocal-db
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${N8N_DB_PASSWORD}
- TWENTY_API_KEY=${TWENTY_API_KEY}
- LISTMONK_CREDENTIALS=${LISTMONK_CREDENTIALS}
- N8N_SMTP_HOST=${SMTP_HOST}
- N8N_SMTP_PORT=${SMTP_PORT}
- N8N_SMTP_USER=${SMTP_USER}
- N8N_SMTP_PASS=${SMTP_PASS}
- N8N_SMTP_SENDER=${SMTP_SENDER}
- N8N_EMAIL_MODE=smtp
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- n8n-cosmolocal-data:/home/node/.n8n
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n-cosmolocal.rule=Host(`automate.cosmolocal.world`)"
- "traefik.http.routers.n8n-cosmolocal.entrypoints=web"
- "traefik.http.services.n8n-cosmolocal.loadbalancer.server.port=5678"
- "traefik.http.routers.n8n-cosmolocal-secure.rule=Host(`automate.cosmolocal.world`)"
- "traefik.http.routers.n8n-cosmolocal-secure.entrypoints=websecure"
- "traefik.http.routers.n8n-cosmolocal-secure.tls=true"
- "traefik.http.routers.n8n-cosmolocal-secure.service=n8n-cosmolocal"
depends_on:
n8n-cosmolocal-db:
condition: service_healthy
networks:
- traefik-public
- cosmolocal-internal
n8n-cosmolocal-db:
image: postgres:16-alpine
container_name: n8n-cosmolocal-db
restart: unless-stopped
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=${N8N_DB_PASSWORD}
volumes:
- n8n-cosmolocal-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
networks:
- cosmolocal-internal
volumes:
n8n-cosmolocal-data:
n8n-cosmolocal-db:
networks:
traefik-public:
external: true
cosmolocal-internal:

View File

@ -0,0 +1,207 @@
{
"name": "Contact Intake — Form to CRM",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "contact-intake",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook-trigger",
"name": "Webhook — Contact Form",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [240, 300],
"webhookId": "contact-intake"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "check-email",
"leftValue": "={{ $json.body.email }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty"
}
},
{
"id": "check-name",
"leftValue": "={{ $json.body.name }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "validate-input",
"name": "Validate Input",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [460, 300]
},
{
"parameters": {
"method": "POST",
"url": "=https://crm.cosmolocal.world/api/v1/objects/people",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"name\": {\n \"firstName\": \"{{ $json.body.name.split(' ')[0] }}\",\n \"lastName\": \"{{ $json.body.name.split(' ').slice(1).join(' ') || '' }}\"\n },\n \"emails\": {\n \"primaryEmail\": \"{{ $json.body.email }}\"\n },\n \"phones\": {\n \"primaryPhone\": \"{{ $json.body.phone || '' }}\"\n }\n}",
"options": {}
},
"id": "create-crm-contact",
"name": "Create Contact in Twenty CRM",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [680, 240]
},
{
"parameters": {
"method": "POST",
"url": "=https://crm.cosmolocal.world/api/v1/objects/notes",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"title\": \"Contact Form Submission\",\n \"body\": \"{{ $('Webhook — Contact Form').item.json.body.message || 'No message provided' }}\",\n \"noteTargets\": [{\n \"personId\": \"{{ $json.data.id }}\"\n }]\n}",
"options": {}
},
"id": "add-note",
"name": "Add Note to Contact",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [900, 240]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={ \"success\": true, \"message\": \"Contact created successfully\" }",
"options": {
"responseCode": 200
}
},
"id": "respond-success",
"name": "Respond — Success",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [1120, 240]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={ \"success\": false, \"message\": \"Missing required fields: name and email\" }",
"options": {
"responseCode": 400
}
},
"id": "respond-error",
"name": "Respond — Validation Error",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [680, 420]
}
],
"connections": {
"Webhook — Contact Form": {
"main": [
[
{
"node": "Validate Input",
"type": "main",
"index": 0
}
]
]
},
"Validate Input": {
"main": [
[
{
"node": "Create Contact in Twenty CRM",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond — Validation Error",
"type": "main",
"index": 0
}
]
]
},
"Create Contact in Twenty CRM": {
"main": [
[
{
"node": "Add Note to Contact",
"type": "main",
"index": 0
}
]
]
},
"Add Note to Contact": {
"main": [
[
{
"node": "Respond — Success",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "cosmolocal"
},
{
"name": "crm"
}
],
"pinData": {}
}

View File

@ -0,0 +1,257 @@
{
"name": "Lead Nurturing — Welcome Email Sequence",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "new-contact-created",
"options": {}
},
"id": "webhook-new-contact",
"name": "Webhook — New Contact Created",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [240, 300],
"webhookId": "new-contact-created"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "set-email",
"name": "email",
"value": "={{ $json.body.email || $json.body.emails?.primaryEmail }}",
"type": "string"
},
{
"id": "set-first-name",
"name": "firstName",
"value": "={{ $json.body.firstName || $json.body.name?.firstName || 'there' }}",
"type": "string"
},
{
"id": "set-contact-id",
"name": "contactId",
"value": "={{ $json.body.id || $json.body.contactId }}",
"type": "string"
}
]
},
"options": {}
},
"id": "extract-fields",
"name": "Extract Contact Fields",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [460, 300]
},
{
"parameters": {
"fromEmail": "={{ $env.N8N_SMTP_SENDER }}",
"toEmail": "={{ $json.email }}",
"subject": "Welcome to the Cosmolocal Foundation",
"emailType": "html",
"html": "<h2>Welcome, {{ $json.firstName }}!</h2><p>Thank you for connecting with the Cosmolocal Foundation. We're building a world where local communities thrive within regenerative economies, connected through global knowledge-sharing and commons-based collaboration.</p><p><strong>\"What is heavy should be local, and what is light should be global and shared.\"</strong></p><p>Here's what we're working on:</p><ul><li><strong>Decentralized Governance</strong> — Transparent, community-led decision-making</li><li><strong>Open Knowledge Commons</strong> — Sharing sustainable production methods globally</li><li><strong>Commons-Compatible Capital</strong> — Financing transformative local projects</li><li><strong>Cosmolocal Coordination</strong> — Connecting local efforts into a global network</li></ul><p>We'll be in touch with updates on our progress and opportunities to get involved.</p><p>Warm regards,<br/>The Cosmolocal Foundation Team</p><p style='color:#78716c;font-size:12px;'>cosmolocal.world</p>",
"options": {}
},
"id": "send-welcome-email",
"name": "Send Welcome Email (Day 0)",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [680, 300],
"credentials": {
"smtp": {
"id": "mailcow-smtp",
"name": "Mailcow SMTP"
}
}
},
{
"parameters": {
"amount": 3,
"unit": "days"
},
"id": "wait-3-days",
"name": "Wait 3 Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [900, 300]
},
{
"parameters": {
"fromEmail": "={{ $env.N8N_SMTP_SENDER }}",
"toEmail": "={{ $('Extract Contact Fields').item.json.email }}",
"subject": "Our Strategic Priorities — How We're Building Change",
"emailType": "html",
"html": "<h2>Hi {{ $('Extract Contact Fields').item.json.firstName }},</h2><p>We wanted to share more about our strategic approach to systemic transformation.</p><p>The Cosmolocal Foundation is executing on eight key initiatives:</p><ol><li><strong>Mapping Regenerative Communities</strong> — Cataloging eco-villages, circular economy hubs, and governance initiatives worldwide</li><li><strong>Web3 Funding</strong> — Quadratic funding, DAOs, and Collaborative Finance for local projects</li><li><strong>Open Resources</strong> — A global knowledge commons of governance models and production blueprints</li><li><strong>Education & Advocacy</strong> — Engaging policymakers to scale cosmolocal principles</li><li><strong>Pilots & Grants</strong> — Funding projects that demonstrate scalable solutions</li><li><strong>Cosmolocal Certification</strong> — Blockchain-verified standards for cosmolocal initiatives</li><li><strong>Global Alliances</strong> — Bioregional collaboration on governance and mutual aid</li><li><strong>Impact Research</strong> — Evidence-based scaling through the Cosmolocal Research Institute</li></ol><p>Want to learn more or get involved? Reply to this email — we'd love to hear from you.</p><p>Best,<br/>The Cosmolocal Foundation Team</p>",
"options": {}
},
"id": "send-followup-email",
"name": "Send Follow-up Email (Day 3)",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [1120, 300],
"credentials": {
"smtp": {
"id": "mailcow-smtp",
"name": "Mailcow SMTP"
}
}
},
{
"parameters": {
"amount": 7,
"unit": "days"
},
"id": "wait-7-days",
"name": "Wait 7 Days",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [1340, 300]
},
{
"parameters": {
"fromEmail": "={{ $env.N8N_SMTP_SENDER }}",
"toEmail": "={{ $('Extract Contact Fields').item.json.email }}",
"subject": "Join the Movement — Ways to Participate",
"emailType": "html",
"html": "<h2>Hi {{ $('Extract Contact Fields').item.json.firstName }},</h2><p>We believe in the power of collaboration. Here are ways you can participate in the cosmolocal movement:</p><ul><li><strong>Subscribe to our newsletter</strong> — Stay updated on projects, pilots, and governance experiments</li><li><strong>Join a working group</strong> — Contribute your expertise to research, technology, or community building</li><li><strong>Support a pilot project</strong> — Help fund or participate in cosmolocal demonstrations</li><li><strong>Spread the word</strong> — Share our vision with your network</li></ul><p>Visit <a href='https://cosmolocal.world'>cosmolocal.world</a> to explore our work, or simply reply to this email to start a conversation.</p><p>Together we can build regenerative economies that serve communities and the planet.</p><p>In solidarity,<br/>The Cosmolocal Foundation Team</p>",
"options": {}
},
"id": "send-engagement-email",
"name": "Send Engagement Email (Day 10)",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [1560, 300],
"credentials": {
"smtp": {
"id": "mailcow-smtp",
"name": "Mailcow SMTP"
}
}
},
{
"parameters": {
"method": "PATCH",
"url": "=https://crm.cosmolocal.world/api/v1/objects/people/{{ $('Extract Contact Fields').item.json.contactId }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"stage\": \"NURTURE_COMPLETE\"\n}",
"options": {
"ignore_ssl_issues": false
}
},
"id": "update-crm-stage",
"name": "Update CRM — Nurture Complete",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1780, 300]
}
],
"connections": {
"Webhook — New Contact Created": {
"main": [
[
{
"node": "Extract Contact Fields",
"type": "main",
"index": 0
}
]
]
},
"Extract Contact Fields": {
"main": [
[
{
"node": "Send Welcome Email (Day 0)",
"type": "main",
"index": 0
}
]
]
},
"Send Welcome Email (Day 0)": {
"main": [
[
{
"node": "Wait 3 Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 3 Days": {
"main": [
[
{
"node": "Send Follow-up Email (Day 3)",
"type": "main",
"index": 0
}
]
]
},
"Send Follow-up Email (Day 3)": {
"main": [
[
{
"node": "Wait 7 Days",
"type": "main",
"index": 0
}
]
]
},
"Wait 7 Days": {
"main": [
[
{
"node": "Send Engagement Email (Day 10)",
"type": "main",
"index": 0
}
]
]
},
"Send Engagement Email (Day 10)": {
"main": [
[
{
"node": "Update CRM — Nurture Complete",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "cosmolocal"
},
{
"name": "email"
}
],
"pinData": {}
}

View File

@ -0,0 +1,284 @@
{
"name": "Newsletter Sync — CRM to Listmonk",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 6,
"triggerAtMinute": 0
}
]
}
},
"id": "schedule-trigger",
"name": "Daily Sync (6 AM)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [240, 300]
},
{
"parameters": {
"method": "GET",
"url": "https://crm.cosmolocal.world/api/v1/objects/people",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "limit",
"value": "100"
}
]
},
"options": {}
},
"id": "fetch-crm-contacts",
"name": "Fetch CRM Contacts",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 300]
},
{
"parameters": {
"fieldToSplitOut": "data.people",
"options": {}
},
"id": "split-contacts",
"name": "Split Into Items",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [680, 300]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "has-email",
"leftValue": "={{ $json.emails?.primaryEmail }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "filter-with-email",
"name": "Filter — Has Email",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [900, 300]
},
{
"parameters": {
"method": "GET",
"url": "=http://listmonk-cosmolocal:9000/api/subscribers",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Basic {{ Buffer.from($env.LISTMONK_CREDENTIALS || 'admin:changeme').toString('base64') }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "query",
"value": "=subscribers.email = '{{ $json.emails.primaryEmail }}'"
},
{
"name": "page",
"value": "1"
},
{
"name": "per_page",
"value": "1"
}
]
},
"options": {}
},
"id": "check-listmonk-exists",
"name": "Check If Subscriber Exists",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1120, 240]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "not-exists",
"leftValue": "={{ $json.data?.total }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-new-subscriber",
"name": "If New Subscriber",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1340, 240]
},
{
"parameters": {
"method": "POST",
"url": "http://listmonk-cosmolocal:9000/api/subscribers",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Basic {{ Buffer.from($env.LISTMONK_CREDENTIALS || 'admin:changeme').toString('base64') }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"email\": \"{{ $('Filter — Has Email').item.json.emails.primaryEmail }}\",\n \"name\": \"{{ $('Filter — Has Email').item.json.name?.firstName || '' }} {{ $('Filter — Has Email').item.json.name?.lastName || '' }}\",\n \"status\": \"enabled\",\n \"lists\": [1],\n \"preconfirm_subscriptions\": true,\n \"attribs\": {\n \"source\": \"twenty-crm-sync\",\n \"crm_id\": \"{{ $('Filter — Has Email').item.json.id }}\"\n }\n}",
"options": {}
},
"id": "create-subscriber",
"name": "Create Listmonk Subscriber",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1560, 180]
},
{
"parameters": {},
"id": "no-op-exists",
"name": "Already Subscribed — Skip",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1560, 360]
}
],
"connections": {
"Daily Sync (6 AM)": {
"main": [
[
{
"node": "Fetch CRM Contacts",
"type": "main",
"index": 0
}
]
]
},
"Fetch CRM Contacts": {
"main": [
[
{
"node": "Split Into Items",
"type": "main",
"index": 0
}
]
]
},
"Split Into Items": {
"main": [
[
{
"node": "Filter — Has Email",
"type": "main",
"index": 0
}
]
]
},
"Filter — Has Email": {
"main": [
[
{
"node": "Check If Subscriber Exists",
"type": "main",
"index": 0
}
],
[]
]
},
"Check If Subscriber Exists": {
"main": [
[
{
"node": "If New Subscriber",
"type": "main",
"index": 0
}
]
]
},
"If New Subscriber": {
"main": [
[
{
"node": "Create Listmonk Subscriber",
"type": "main",
"index": 0
}
],
[
{
"node": "Already Subscribed — Skip",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "cosmolocal"
},
{
"name": "newsletter"
}
],
"pinData": {}
}

View File

@ -0,0 +1,230 @@
{
"name": "Follow-up Reminders — Stale Contacts",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9,
"triggerAtMinute": 0,
"triggerAtDay": 1
}
]
}
},
"id": "weekly-schedule",
"name": "Weekly Check (Monday 9 AM)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [240, 300]
},
{
"parameters": {
"method": "GET",
"url": "https://crm.cosmolocal.world/api/v1/objects/people",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "limit",
"value": "100"
}
]
},
"options": {}
},
"id": "fetch-all-contacts",
"name": "Fetch All Contacts",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 300]
},
{
"parameters": {
"fieldToSplitOut": "data.people",
"options": {}
},
"id": "split-contacts",
"name": "Split Into Items",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [680, 300]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst staleItems = [];\nconst now = new Date();\nconst STALE_DAYS = 14;\n\nfor (const item of items) {\n const updatedAt = new Date(item.json.updatedAt);\n const daysSinceUpdate = Math.floor((now - updatedAt) / (1000 * 60 * 60 * 24));\n \n if (daysSinceUpdate >= STALE_DAYS) {\n staleItems.push({\n json: {\n ...item.json,\n daysSinceUpdate,\n email: item.json.emails?.primaryEmail || 'N/A',\n fullName: `${item.json.name?.firstName || ''} ${item.json.name?.lastName || ''}`.trim() || 'Unknown'\n }\n });\n }\n}\n\nreturn staleItems.length > 0 ? staleItems : [{ json: { noStaleContacts: true } }];"
},
"id": "filter-stale",
"name": "Filter Stale Contacts (14+ days)",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [900, 300]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "has-stale",
"leftValue": "={{ $json.noStaleContacts }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notTrue"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-has-stale",
"name": "Any Stale Contacts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1120, 300]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst count = items.length;\n\nconst tableRows = items.map(i => `<tr><td style=\"padding:8px;border:1px solid #e7e5e4\">${i.json.fullName}</td><td style=\"padding:8px;border:1px solid #e7e5e4\">${i.json.email}</td><td style=\"padding:8px;border:1px solid #e7e5e4\">${i.json.daysSinceUpdate}</td></tr>`).join('');\n\nreturn [{\n json: {\n subject: `[Cosmolocal CRM] ${count} contact${count !== 1 ? 's' : ''} need${count === 1 ? 's' : ''} follow-up`,\n htmlBody: `<h2>Stale Contact Report</h2><p>The following ${count} contact${count !== 1 ? 's have' : ' has'} not been updated in 14+ days:</p><table style=\"border-collapse:collapse;width:100%\"><tr style=\"background:#f5f5f4\"><th style=\"padding:8px;text-align:left;border:1px solid #e7e5e4\">Name</th><th style=\"padding:8px;text-align:left;border:1px solid #e7e5e4\">Email</th><th style=\"padding:8px;text-align:left;border:1px solid #e7e5e4\">Days Stale</th></tr>${tableRows}</table><p style=\"margin-top:16px\"><a href=\"https://crm.cosmolocal.world\">Open CRM</a> to follow up.</p><p style=\"color:#78716c;font-size:12px\">Automated by n8n — automate.cosmolocal.world</p>`,\n count\n }\n}];"
},
"id": "build-report",
"name": "Build Report Email",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1340, 240]
},
{
"parameters": {
"fromEmail": "={{ $env.N8N_SMTP_SENDER }}",
"toEmail": "={{ $env.N8N_SMTP_SENDER }}",
"subject": "={{ $json.subject }}",
"emailType": "html",
"html": "={{ $json.htmlBody }}",
"options": {}
},
"id": "send-reminder-email",
"name": "Send Reminder to Team",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [1560, 240],
"credentials": {
"smtp": {
"id": "mailcow-smtp",
"name": "Mailcow SMTP"
}
}
},
{
"parameters": {},
"id": "no-stale-contacts",
"name": "No Stale Contacts — Done",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1340, 420]
}
],
"connections": {
"Weekly Check (Monday 9 AM)": {
"main": [
[
{
"node": "Fetch All Contacts",
"type": "main",
"index": 0
}
]
]
},
"Fetch All Contacts": {
"main": [
[
{
"node": "Split Into Items",
"type": "main",
"index": 0
}
]
]
},
"Split Into Items": {
"main": [
[
{
"node": "Filter Stale Contacts (14+ days)",
"type": "main",
"index": 0
}
]
]
},
"Filter Stale Contacts (14+ days)": {
"main": [
[
{
"node": "Any Stale Contacts?",
"type": "main",
"index": 0
}
]
]
},
"Any Stale Contacts?": {
"main": [
[
{
"node": "Build Report Email",
"type": "main",
"index": 0
}
],
[
{
"node": "No Stale Contacts — Done",
"type": "main",
"index": 0
}
]
]
},
"Build Report Email": {
"main": [
[
{
"node": "Send Reminder to Team",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "cosmolocal"
},
{
"name": "crm"
}
],
"pinData": {}
}

View File

@ -0,0 +1,347 @@
{
"name": "Webhook Events — Gitea/GitHub to CRM",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "git-events",
"options": {}
},
"id": "webhook-git",
"name": "Webhook — Git Events",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [240, 300],
"webhookId": "git-events"
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst headers = $input.first().json.headers;\n\n// Detect source (Gitea vs GitHub)\nconst isGitea = headers['x-gitea-event'] !== undefined;\nconst isGitHub = headers['x-github-event'] !== undefined;\n\nconst eventType = isGitea \n ? headers['x-gitea-event'] \n : headers['x-github-event'] || 'unknown';\n\nlet result = {\n source: isGitea ? 'gitea' : isGitHub ? 'github' : 'unknown',\n eventType,\n repo: body.repository?.full_name || body.repository?.name || 'unknown',\n action: body.action || 'push',\n sender: body.sender?.login || body.pusher?.name || 'unknown',\n senderEmail: body.sender?.email || body.pusher?.email || '',\n url: '',\n title: '',\n description: ''\n};\n\nswitch (eventType) {\n case 'push':\n const commits = body.commits || [];\n result.title = `Push: ${commits.length} commit(s) to ${result.repo}`;\n result.description = commits.map(c => `- ${c.message}`).join('\\n');\n result.url = body.compare_url || body.compare || '';\n break;\n case 'issues':\n result.title = `Issue ${body.action}: ${body.issue?.title}`;\n result.description = body.issue?.body || '';\n result.url = body.issue?.html_url || '';\n break;\n case 'pull_request':\n result.title = `PR ${body.action}: ${body.pull_request?.title}`;\n result.description = body.pull_request?.body || '';\n result.url = body.pull_request?.html_url || '';\n break;\n case 'create':\n result.title = `Created ${body.ref_type}: ${body.ref}`;\n result.description = `New ${body.ref_type} created in ${result.repo}`;\n break;\n case 'star':\n case 'watch':\n result.title = `${result.sender} starred ${result.repo}`;\n result.description = `Repository now has ${body.repository?.stars_count || body.repository?.stargazers_count || '?'} stars`;\n break;\n default:\n result.title = `${eventType} event on ${result.repo}`;\n result.description = JSON.stringify(body).substring(0, 500);\n}\n\nreturn [{ json: result }];"
},
"id": "parse-event",
"name": "Parse Git Event",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [460, 300]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "is-significant",
"leftValue": "={{ $json.eventType }}",
"rightValue": "push,issues,pull_request,star",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "filter-significant",
"name": "Filter Significant Events",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [680, 300]
},
{
"parameters": {
"method": "POST",
"url": "https://crm.cosmolocal.world/api/v1/objects/notes",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"title\": \"[{{ $json.source }}] {{ $json.title }}\",\n \"body\": \"**Event**: {{ $json.eventType }}\\n**Repo**: {{ $json.repo }}\\n**By**: {{ $json.sender }}\\n\\n{{ $json.description }}\\n\\n{{ $json.url ? '[View on ' + $json.source + '](' + $json.url + ')' : '' }}\"\n}",
"options": {}
},
"id": "log-to-crm",
"name": "Log Activity to CRM",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [900, 240]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "has-email",
"leftValue": "={{ $('Parse Git Event').item.json.senderEmail }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "has-sender-email",
"name": "Sender Has Email?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1120, 240]
},
{
"parameters": {
"method": "GET",
"url": "https://crm.cosmolocal.world/api/v1/objects/people",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "filter",
"value": "={\"emails\":{\"primaryEmail\":{\"eq\":\"{{ $('Parse Git Event').item.json.senderEmail }}\"}}}"
}
]
},
"options": {}
},
"id": "find-contact",
"name": "Find Contact by Email",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1340, 180]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "contact-found",
"leftValue": "={{ $json.data?.people?.length }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "contact-exists",
"name": "Contact Found?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1560, 180]
},
{
"parameters": {
"method": "PATCH",
"url": "=https://crm.cosmolocal.world/api/v1/objects/people/{{ $json.data.people[0].id }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"updatedAt\": \"{{ new Date().toISOString() }}\"\n}",
"options": {}
},
"id": "touch-contact",
"name": "Touch Contact — Update Timestamp",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1780, 120]
},
{
"parameters": {
"method": "POST",
"url": "https://crm.cosmolocal.world/api/v1/objects/people",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{ $env.TWENTY_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"name\": {\n \"firstName\": \"{{ $('Parse Git Event').item.json.sender }}\",\n \"lastName\": \"\"\n },\n \"emails\": {\n \"primaryEmail\": \"{{ $('Parse Git Event').item.json.senderEmail }}\"\n }\n}",
"options": {}
},
"id": "create-contributor-contact",
"name": "Create Contributor Contact",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1780, 280]
},
{
"parameters": {},
"id": "no-op-skip",
"name": "Skip — Not Significant",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [900, 420]
}
],
"connections": {
"Webhook — Git Events": {
"main": [
[
{
"node": "Parse Git Event",
"type": "main",
"index": 0
}
]
]
},
"Parse Git Event": {
"main": [
[
{
"node": "Filter Significant Events",
"type": "main",
"index": 0
}
]
]
},
"Filter Significant Events": {
"main": [
[
{
"node": "Log Activity to CRM",
"type": "main",
"index": 0
}
],
[
{
"node": "Skip — Not Significant",
"type": "main",
"index": 0
}
]
]
},
"Log Activity to CRM": {
"main": [
[
{
"node": "Sender Has Email?",
"type": "main",
"index": 0
}
]
]
},
"Sender Has Email?": {
"main": [
[
{
"node": "Find Contact by Email",
"type": "main",
"index": 0
}
],
[]
]
},
"Find Contact by Email": {
"main": [
[
{
"node": "Contact Found?",
"type": "main",
"index": 0
}
]
]
},
"Contact Found?": {
"main": [
[
{
"node": "Touch Contact — Update Timestamp",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Contributor Contact",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "cosmolocal"
},
{
"name": "git"
},
{
"name": "crm"
}
],
"pinData": {}
}

70
n8n-workflows/README.md Normal file
View File

@ -0,0 +1,70 @@
# Cosmolocal n8n Workflows
Import these JSON files into [automate.cosmolocal.world](https://automate.cosmolocal.world) via **Settings > Import Workflow**.
## Setup Requirements
API keys are passed as **Docker environment variables** in `docker-compose.yml` (n8n community edition doesn't support Settings > Variables). The workflows access them via `$env.VARIABLE_NAME`.
| Variable | Description |
|----------|-------------|
| `TWENTY_API_KEY` | CRM API key |
| `SMTP_HOST` | SMTP server hostname |
| `SMTP_PORT` | SMTP server port |
| `SMTP_USER` | SMTP username |
| `SMTP_PASS` | SMTP password |
| `SMTP_SENDER` | From address for outgoing email |
| `LISTMONK_CREDENTIALS` | Listmonk `user:pass` |
| `N8N_DB_PASSWORD` | n8n PostgreSQL password |
All credentials are stored in `.env` on the server (gitignored). See server admin for values.
## Workflows
### 01 — Contact Intake (Form to CRM)
**Trigger:** Webhook POST to `/webhook/contact-intake`
**Flow:** Validate input > Create contact in Twenty CRM > Add note with message > Respond
Use this webhook URL in your website contact form:
```
https://automate.cosmolocal.world/webhook/contact-intake
```
POST body:
```json
{
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+1234567890",
"message": "Interested in collaborating"
}
```
### 02 — Lead Nurturing (Welcome Email Sequence)
**Trigger:** Webhook POST to `/webhook/new-contact-created`
**Flow:** Day 0: Welcome email > Day 3: Strategic priorities > Day 10: Ways to participate > Update CRM stage
Chain this from workflow 01 or call it when a new contact is created in the CRM.
### 03 — Newsletter Sync (CRM to Listmonk)
**Trigger:** Daily at 6:00 AM
**Flow:** Fetch CRM contacts > Check if already in Listmonk > Create new subscribers
Syncs all CRM contacts with email addresses to Listmonk list #1. Adjust the list ID if needed.
### 04 — Follow-up Reminders (Stale Contacts)
**Trigger:** Weekly on Monday at 9:00 AM
**Flow:** Fetch contacts > Filter those not updated in 14+ days > Email report to team
Sends an HTML table report to the team with stale contacts and a link to the CRM.
### 05 — Webhook Events (Git to CRM)
**Trigger:** Webhook POST to `/webhook/git-events`
**Flow:** Parse Gitea/GitHub event > Log as CRM note > Find or create contributor contact
Add this webhook URL to Gitea/GitHub repos:
```
https://automate.cosmolocal.world/webhook/git-events
```
Supports: push, issues, pull_request, star/watch events. Auto-detects Gitea vs GitHub from headers.

View File

@ -1,7 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: "standalone",
};
export default nextConfig;

0
public/.gitkeep Normal file
View File

View File

@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

View File

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

View File

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

View File

@ -1,26 +1,69 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
--background: #fafaf9;
--foreground: #1c1917;
--accent: #4f7942;
--accent-light: #6b9f5b;
--muted: #78716c;
--border: #e7e5e4;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-accent: var(--accent);
--color-accent-light: var(--accent-light);
--color-muted: var(--muted);
--color-border: var(--border);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: #0c0a09;
--foreground: #e7e5e4;
--accent: #6b9f5b;
--accent-light: #4f7942;
--muted: #a8a29e;
--border: #292524;
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
/* Hero section */
.hero-section {
background: linear-gradient(
145deg,
#1a2e1a 0%,
#2d4a2d 25%,
#1c3a2a 50%,
#1a2835 75%,
#0f1f2e 100%
);
}
.hero-glow {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 50% 0%, rgba(107, 159, 91, 0.2) 0%, transparent 70%),
radial-gradient(ellipse 40% 40% at 20% 80%, rgba(79, 121, 66, 0.12) 0%, transparent 60%),
radial-gradient(ellipse 40% 40% at 80% 60%, rgba(30, 80, 100, 0.1) 0%, transparent 60%);
}
.hero-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(ellipse 70% 70% at 50% 50%, black 20%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse 70% 70% at 50% 50%, black 20%, transparent 80%);
}

View File

@ -13,8 +13,17 @@ const geistMono = Geist_Mono({
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Cosmolocal Foundation",
description:
"The Cosmolocal Foundation empowers communities to build localized, regenerative economies connected through global knowledge-sharing, commons-based collaboration, and decentralized governance.",
openGraph: {
title: "Cosmolocal Foundation",
description:
"What is heavy should be local, and what is light should be global and shared.",
url: "https://cosmolocal.world",
siteName: "Cosmolocal Foundation",
type: "website",
},
};
export default function RootLayout({

View File

@ -1,65 +1,488 @@
import Image from "next/image";
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
<div className="flex min-h-screen flex-col">
{/* Nav */}
<header className="border-b border-border sticky top-0 z-50 bg-background/80 backdrop-blur-sm">
<nav className="mx-auto flex max-w-5xl items-center justify-between px-6 py-4">
<a href="#" className="text-lg font-semibold tracking-tight">
Cosmolocal Foundation
</a>
<div className="flex items-center gap-6 text-sm text-muted">
<a href="#vision" className="hidden sm:inline transition-colors hover:text-foreground">
Vision
</a>
<a href="#mission" className="hidden sm:inline transition-colors hover:text-foreground">
Mission
</a>
<a href="#priorities" className="hidden sm:inline transition-colors hover:text-foreground">
Priorities
</a>
<a href="#founder" className="hidden sm:inline transition-colors hover:text-foreground">
Founder
</a>
<a href="#contact" className="transition-colors hover:text-foreground">
Contact
</a>
<a
href="https://docs.cosmolocal.world"
className="rounded-full bg-accent px-4 py-1.5 text-white transition-colors hover:bg-accent-light"
>
Docs
</a>
</div>
</nav>
</header>
{/* Hero */}
<section className="hero-section relative flex flex-1 flex-col items-center justify-center overflow-hidden px-6 py-28 text-center sm:py-36">
<div className="hero-glow" aria-hidden="true" />
<div className="hero-grid" aria-hidden="true" />
<div className="relative z-10">
<blockquote className="text-lg italic text-white/70 sm:text-xl">
&ldquo;What is heavy should be local, and what is light should be global and shared.&rdquo;
</blockquote>
<h1 className="mt-8 max-w-3xl text-4xl font-bold leading-tight tracking-tight text-white sm:text-5xl">
Cosmolocal Foundation
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<p className="mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-white/75">
Empowering communities to build localized, regenerative economies
connected through global knowledge-sharing and commons-based
collaboration.
</p>
<div className="mt-10 flex justify-center gap-4">
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
href="#vision"
className="rounded-full bg-white px-6 py-2.5 text-sm font-medium text-stone-900 transition-colors hover:bg-white/90"
>
Templates
</a>{" "}
or the{" "}
Learn More
</a>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
href="https://docs.cosmolocal.world"
className="rounded-full border border-white/25 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-white/10"
>
Learning
</a>{" "}
center.
Documentation
</a>
</div>
</div>
</section>
{/* Vision */}
<section id="vision" className="border-t border-border bg-foreground/[0.02]">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">Vision</h2>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
The unfolding ecological crisis and the broader metacrisis
necessitate a fundamental reorientation of our productive forces and
financial coordination. We must transition toward production models
that prioritize long-term ecological and social resilience rather
than short-term efficiency, balancing local autonomy with global
cooperation.
</p>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
The Foundation bridges this gap by connecting localized regenerative
efforts with global knowledge commons and decentralized financial
coordination &mdash; scaling physically rooted projects with the
coordination capacity available through Web3 technologies.
</p>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
To achieve systemic transformation, we must relocalize production,
de-risk supply chains, and prioritize economies of scope over
economies of scale. Traditional globalization has led to fragile
supply networks &mdash; the Cosmolocal Foundation addresses this by
fostering bioregional resiliency through global collaboration.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
</section>
{/* Mission */}
<section id="mission" className="border-t border-border">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">Mission</h2>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
We aim to empower communities to build localized, regenerative
economies by providing open-source tools, funding, and global
coordination networks. Through strategic investments, pilot
projects, and policy advocacy, we foster:
</p>
<div className="mt-12 grid gap-8 sm:grid-cols-2">
<div className="rounded-lg border border-border p-6">
<h3 className="font-semibold">Decentralized Governance</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Transparent, community-led decision-making that distributes
power equitably and ensures accountability at every level.
</p>
</div>
<div className="rounded-lg border border-border p-6">
<h3 className="font-semibold">Open Knowledge Commons</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Sharing sustainable production methods, governance models, and
regenerative blueprints globally &mdash; free and accessible to all.
</p>
</div>
<div className="rounded-lg border border-border p-6">
<h3 className="font-semibold">Commons-Compatible Capital</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Legal and property forms adapted to commons requirements,
financing transformative local projects without extractive
conditions.
</p>
</div>
<div className="rounded-lg border border-border p-6">
<h3 className="font-semibold">Cosmolocal Coordination</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Connecting localized efforts into a global, impact-driven
network where knowledge flows freely while production remains
rooted in place.
</p>
</div>
</div>
</div>
</section>
{/* Strategic Priorities */}
<section id="priorities" className="border-t border-border bg-foreground/[0.02]">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">
Strategic Priorities
</h2>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
To achieve scalable impact, the Foundation executes these key
initiatives:
</p>
<div className="mt-12 grid gap-6 sm:grid-cols-2">
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
1
</span>
<div>
<h3 className="font-semibold">Mapping Regenerative Communities</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Identifying and cataloging eco-villages, circular economy hubs,
and decentralized governance initiatives worldwide to find
strategic points of leverage for intervention.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
2
</span>
<div>
<h3 className="font-semibold">Web3 Funding</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Connecting local projects with crypto-native financial
mechanisms such as quadratic funding, DAOs, and Collaborative
Finance.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
3
</span>
<div>
<h3 className="font-semibold">Open Resources</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Developing a global knowledge commons of decentralized
governance models, regenerative production blueprints, and
circular economy tools.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
4
</span>
<div>
<h3 className="font-semibold">Education & Advocacy</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Producing educational content and engaging policymakers to
scale cosmolocal principles in governance and finance.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
5
</span>
<div>
<h3 className="font-semibold">Pilots & Grants Program</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Funding and launching projects that demonstrate scalable
cosmolocal solutions, integrating blockchain transparency and
commons-based innovation.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
6
</span>
<div>
<h3 className="font-semibold">Cosmolocal Certification</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Establishing credibility and standards for cosmolocal
initiatives, verified through decentralized, blockchain-based
compliance mechanisms.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
7
</span>
<div>
<h3 className="font-semibold">Global Alliances</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Building bioregional alliances to facilitate transnational
collaboration on governance, resource management, and mutual
aid.
</p>
</div>
</div>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<div className="flex items-start gap-3">
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent">
8
</span>
<div>
<h3 className="font-semibold">Impact Research & Iteration</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Establishing a Cosmolocal Research Institute to track success
metrics, iterate on best practices, and drive evidence-based
scaling strategies.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Why Support */}
<section id="support" className="border-t border-border">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">
Why Support the Foundation?
</h2>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
Supporters play a pivotal role in scaling decentralized,
community-driven economies. The Foundation creates systemic
transformation by bridging regenerative finance with real-world
social and ecological impact.
</p>
<div className="mt-10 grid gap-6 sm:grid-cols-2">
<div className="flex items-start gap-3">
<span className="mt-1 h-2 w-2 shrink-0 rounded-full bg-accent" />
<p className="text-sm leading-relaxed text-muted">
<span className="font-medium text-foreground">Economic self-sufficiency</span>{" "}
and resilience for local communities
</p>
</div>
<div className="flex items-start gap-3">
<span className="mt-1 h-2 w-2 shrink-0 rounded-full bg-accent" />
<p className="text-sm leading-relaxed text-muted">
<span className="font-medium text-foreground">Decentralized governance</span>{" "}
models that distribute power more equitably
</p>
</div>
<div className="flex items-start gap-3">
<span className="mt-1 h-2 w-2 shrink-0 rounded-full bg-accent" />
<p className="text-sm leading-relaxed text-muted">
<span className="font-medium text-foreground">Open-source innovation</span>{" "}
to democratize knowledge and technology
</p>
</div>
<div className="flex items-start gap-3">
<span className="mt-1 h-2 w-2 shrink-0 rounded-full bg-accent" />
<p className="text-sm leading-relaxed text-muted">
<span className="font-medium text-foreground">Commons-based economics</span>{" "}
ensuring long-term sustainable and fair returns
</p>
</div>
</div>
<div className="mt-10 rounded-lg border border-accent/20 bg-accent/5 p-6">
<h3 className="font-semibold">Cosmo-Local Financing Facility</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
The Foundation will establish a special purpose vehicle to create
a seamless bridge between impact-driven investors and local
regenerative projects. This facility allows unrooted financial
capital to flow into productive, regenerative networks, ensuring
sustainable returns through resilient, decentralized economies.
</p>
</div>
</div>
</section>
{/* Founder */}
<section id="founder" className="border-t border-border bg-foreground/[0.02]">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">Founder</h2>
<div className="mt-6 max-w-3xl">
<h3 className="text-xl font-semibold">Michel Bauwens</h3>
<p className="mt-1 text-sm text-accent">
Founder, P2P Foundation
</p>
<p className="mt-4 text-muted leading-relaxed">
Michel Bauwens is the driving force behind the Cosmolocal
Foundation and a pioneering thinker in peer-to-peer economics and
commons-based governance. As the founder of the P2P Foundation, he
has researched and promoted decentralized, collaborative economic
models that empower communities through shared knowledge and
resources, and hosted the first release of the Bitcoin Whitepaper
by Satoshi Nakamoto.
</p>
<p className="mt-4 text-muted leading-relaxed">
Bauwens has led multiple initiatives demonstrating the viability
of cosmolocal approaches, such as the Ghent Commons Transition
Plan, and has advised governments (Ecuador), international
institutions (the Vatican), and grassroots organizations. He
regularly speaks at global conferences about the intersection of
decentralized technology, regenerative economics, and the future
of production and value exchange.
</p>
</div>
</div>
</section>
{/* Core Values */}
<section id="values" className="border-t border-border">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">
Core Values
</h2>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
Guided by our Social Charter, the Foundation upholds these
principles across all initiatives.
</p>
<div className="mt-12 grid gap-8 sm:grid-cols-3">
<div>
<h3 className="font-semibold">Regenerative Socio-Economics</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Promoting systems where local communities pool resources for
shared prosperity, transitioning from extractive, centralized
economies to regenerative, commons-based economies.
</p>
</div>
<div>
<h3 className="font-semibold">Decentralized Systems</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Leveraging Web3 technologies &mdash; blockchain, DAOs, community
currencies &mdash; to foster transparency, inclusivity, and
fairness in governance.
</p>
</div>
<div>
<h3 className="font-semibold">Transnational Collaboration</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Building global alliances to share resources, knowledge, and
strategies across borders, strengthening local resilience
through supportive global networks.
</p>
</div>
</div>
</div>
</section>
{/* Governance */}
<section id="governance" className="border-t border-border bg-foreground/[0.02]">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">Governance</h2>
<p className="mt-4 max-w-3xl text-muted leading-relaxed">
The Foundation operates through transparent, community-led
governance with clear accountability frameworks.
</p>
<div className="mt-10 grid gap-6 sm:grid-cols-2">
<div className="rounded-lg border border-border bg-background p-6">
<h3 className="font-semibold">Transparency & Accountability</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
All financial transactions, governance decisions, and project
outcomes are documented and accessible. Periodic assessments
ensure alignment with cosmolocal principles.
</p>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<h3 className="font-semibold">Collaborative Decision-Making</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Decentralized governance mechanisms with voting for key
decisions. Members operate with autonomy within the
Foundation&apos;s principles.
</p>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<h3 className="font-semibold">Conflict Resolution</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
Disagreements are resolved through open dialogue and mediation,
with escalation paths through the core team in accordance with
shared principles.
</p>
</div>
<div className="rounded-lg border border-border bg-background p-6">
<h3 className="font-semibold">Knowledge Sharing</h3>
<p className="mt-2 text-sm text-muted leading-relaxed">
All team members document and share experiences, innovations,
and lessons learned, contributing to the commons and maintaining
thorough records.
</p>
</div>
</div>
</div>
</section>
{/* Contact */}
<section id="contact" className="border-t border-border">
<div className="mx-auto max-w-5xl px-6 py-20">
<h2 className="text-2xl font-semibold tracking-tight">
Get in Touch
</h2>
<p className="mt-4 max-w-xl text-muted leading-relaxed">
Interested in collaborating, supporting, or learning more about
cosmolocal approaches? We welcome researchers, practitioners,
communities, and impact-driven investors.
</p>
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
href="mailto:hello@cosmolocal.world"
className="mt-6 inline-block rounded-full bg-accent px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-accent-light"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
hello@cosmolocal.world
</a>
</div>
</main>
</section>
{/* Footer */}
<footer className="border-t border-border">
<div className="mx-auto flex max-w-5xl items-center justify-between px-6 py-6 text-sm text-muted">
<span>&copy; {new Date().getFullYear()} Cosmolocal Foundation</span>
<div className="flex items-center gap-6">
<a
href="https://p2pfoundation.net"
className="transition-colors hover:text-foreground"
>
P2P Foundation
</a>
<a
href="https://docs.cosmolocal.world"
className="transition-colors hover:text-foreground"
>
Documentation
</a>
</div>
</div>
</footer>
</div>
);
}