Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Deploy Jekyll site to Pages

on:
push:
branches: ["main"]
workflow_dispatch:
Comment thread
anwar3606 marked this conversation as resolved.

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
- name: Setup Pages
uses: actions/configure-pages@v6
- name: Build with Jekyll
run: bundle exec jekyll build --destination ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v5
with:
path: ./_site

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
.idea
.tags
_site/
.sass-cache/
.jekyll-cache/
.jekyll-metadata
vendor/
15 changes: 13 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ GEM
http_parser.rb (~> 0)
eventmachine (1.2.7)
ffi (1.17.4-x64-mingw-ucrt)
ffi (1.17.4-x86_64-linux-gnu)
forwardable-extended (2.6.0)
google-protobuf (4.35.1-x64-mingw-ucrt)
bigdecimal
rake (~> 13.3)
google-protobuf (4.35.1-x86_64-linux-gnu)
bigdecimal
rake (~> 13.3)
http_parser.rb (0.8.1)
i18n (1.15.2)
concurrent-ruby (~> 1.0)
Expand Down Expand Up @@ -67,13 +71,17 @@ GEM
safe_yaml (1.0.5)
sass-embedded (1.102.0-x64-mingw-ucrt)
google-protobuf (~> 4.31)
sass-embedded (1.102.0-x86_64-linux-gnu)
google-protobuf (~> 4.31)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.6.0)
webrick (1.9.2)

PLATFORMS
x64-mingw-ucrt
x86_64-linux
x86_64-linux-gnu

DEPENDENCIES
jekyll (~> 4.4)
Expand All @@ -82,15 +90,17 @@ CHECKSUMS
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
bundler (4.0.17) sha256=214e21431b5665dd2f99df8a5511c6b151d7a72e8015c8b38f8b775b61cbb6c1
bundler (4.0.20) sha256=7978a8ac648767f5e635bc522445b79e80a52b907a39a36c2d8085ed6bc762ae
colorator (1.1.0) sha256=e2f85daf57af47d740db2a32191d1bdfb0f6503a0dfbc8327d0c9154d5ddfc38
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
em-websocket (0.5.3) sha256=f56a92bde4e6cb879256d58ee31f124181f68f8887bd14d53d5d9a292758c6a8
eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972
ffi (1.17.4-x64-mingw-ucrt) sha256=f6ff9618cfccc494138bddade27aa06c74c6c7bc367a1ea1103d80c2fcb9ed35
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
forwardable-extended (2.6.0) sha256=1bec948c469bbddfadeb3bd90eb8c85f6e627a412a3e852acfd7eaedbac3ec97
google-protobuf (4.35.1-x64-mingw-ucrt) sha256=0660b716c44a4d3baa5cb648cd95dc7faed87ea92cd08fe92cf9156415b35033
google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd
http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
jekyll (4.4.1) sha256=4c1144d857a5b2b80d45b8cf5138289579a9f8136aadfa6dd684b31fe2bc18c1
Expand All @@ -112,9 +122,10 @@ CHECKSUMS
rouge (4.7.0) sha256=dba5896715c0325c362e895460a6d350803dbf6427454f49a47500f3193ea739
safe_yaml (1.0.5) sha256=a6ac2d64b7eb027bdeeca1851fe7e7af0d668e133e8a88066a0c6f7087d9f848
sass-embedded (1.102.0-x64-mingw-ucrt) sha256=651e6e99c7bb486c9531b2e1914a264983f8e4fe49a45b00c2303daec51fc25f
sass-embedded (1.102.0-x86_64-linux-gnu) sha256=9815b7604123a9a44a65d44d5e58e7af1c7005174b745cda83bad4c9e3760ab3
terminal-table (3.0.2) sha256=f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91
unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131

BUNDLED WITH
4.0.17
4.0.20
116 changes: 116 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Provenant Website (`provenant.net`)

Static corporate website for [Provenant](https://provenant.net), built with Jekyll and deployed automatically to GitHub Pages.

---

## 1. Overview & Architecture

```
[Visitor Browser]
│
├─► Static Content & Assets (GitHub Pages / CDN)
│ • Jekyll 4.x static site generator
│ • Automated deployment via GitHub Actions
│
└─► Contact Form Submission (`/contact`)
│
├─► 1. Cloudflare Turnstile Widget (Bot Defense)
│ • In-browser challenge validation
│ • Produces single-use `cf-turnstile-response` token
│
└─► 2. POST https://inbound-inquiries.provenant.net/contact
• Managed via CDK in `origin-infrastructure/utils/website-contact-form`
• Amazon API Gateway (HTTP API) with route-level rate limiting
• AWS Lambda (Node.js 24) verifies token with Cloudflare API
• Dispatches email notifications via Amazon SES to `info@provenant.net`
```

---

## 2. Contact Form & Anti-Bot Protection

The contact page (`contact.html`) integrates Cloudflare Turnstile and an AWS serverless backend to eliminate form spam without intrusive CAPTCHAs.

### Frontend Integration (`contact.html` & `assets/js/contact.js`)

1. **Turnstile Script & Widget**:
The Cloudflare Turnstile API script is loaded asynchronously in `contact.html`:
```html
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
```
The widget is mounted inside the form:
```html
<div class="cf-turnstile" data-sitekey="<TURNSTILE_SITE_KEY>" data-theme="light"></div>
```

2. **Submission Flow**:
- `assets/js/contact.js` captures form submit events.
- Extracts the user inputs and `cf-turnstile-response` token.
- Submits JSON payload to the API endpoint (`https://inbound-inquiries.provenant.net/contact`).
- If submission fails, `turnstile.reset()` is invoked automatically to issue a fresh token for immediate retry.
- The API URL can be overridden in development environments via `window.CONTACT_API_URL`.

### Backend Infrastructure (`origin-infrastructure`)

The backend stack is managed as Infrastructure as Code using AWS CDK in the `origin-infrastructure` repository under:
```
utils/website-contact-form/
├── README.md
└── cdk/
├── bin/cdk.ts
├── lib/website-contact-form-stack.ts
└── lambda/contact-handler/index.ts
```

- **Amazon API Gateway**: HTTP API with CORS restricted to `https://provenant.net` and `https://www.provenant.net`. Throttling is configured at 2 req/s (burst 5) at the edge.
- **AWS Lambda**: Node.js 24 runtime with reserved concurrency capped at 2. Validates payload fields, verifies the Turnstile token against `https://challenges.cloudflare.com/turnstile/v0/siteverify`, and formats the SES email.
- **AWS Secrets Manager**: Stores the Turnstile secret key under `website-contact-form/turnstile-secret`. The Lambda retrieves and caches this key in memory across warm invocations.
- **Amazon SES**: Sends inquiry notifications to `info@provenant.net` with identity-scoped IAM permissions.

---

## 3. Local Development

### Prerequisites

- Ruby 3.3+
- Bundler (`gem install bundler`)

### Setup & Run

1. Clone repository and install Ruby dependencies:
```bash
bundle install
```

2. Start the local Jekyll server:
```bash
bundle exec jekyll serve
```

3. Open `http://localhost:4000` in your browser.

*Note: The Turnstile widget configured for `provenant-website` includes `localhost` in its allowed domain list, allowing end-to-end form verification during local development.*

---

## 4. Deployment & CI/CD

Deployment is fully automated through GitHub Actions (`.github/workflows/deploy.yml`):

- **Trigger**: Every push to the default branch (`main`) (or a manual `workflow_dispatch`).
- **Build**: Compiles Jekyll assets with `bundle exec jekyll build --destination ./_site`.
- **Deploy**: Packages and uploads the `_site/` directory to GitHub Pages using `actions/deploy-pages`.

### DNS Configuration (Route 53)

Authoritative DNS for `provenant.net` is managed in AWS Route 53:

- **Apex (`provenant.net`)**: A records pointing to GitHub Pages Anycast IPs:
- `185.199.108.153`
- `185.199.109.153`
- `185.199.110.153`
- `185.199.111.153`
- **Subdomain (`www.provenant.net`)**: CNAME pointing to `provenant-dev.github.io`.
- **API Subdomain (`inbound-inquiries.provenant.net`)**: Managed by the CDK stack in `origin-infrastructure`.
8 changes: 7 additions & 1 deletion _config.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
title: Provenant
description: "Authentic Digital Communication"
url: "https://www.provenant.net"
url: "https://provenant.net"
baseurl: ""

markdown: kramdown
permalink: pretty

exclude:
- README.md
- Gemfile
- Gemfile.lock
- _site
- vendor
- .sass-cache
- .jekyll-cache

include:
- ".well-known"
Expand Down
Loading