Builder Notes · BN-002
Building Portal
WORKSHOPTESTINGSECURITY REVIEW PENDINGNOT INSCRIBED
Living publication. Not on Bitcoin. A future exact version may be inscribed.
# Builder Note BN-02: Building Portal
- **Project:** CyberdelicOS
- **Status:** Living, testing, security review pending
- **Version:** 1.0
- **Date:** August 16, 2026
- **Type:** Reference architecture and prototype build guide
- **Primary verbs:** ATTEND, END, FORGET
- **Supporting verbs:** CARRY, RATIFY, WITHDRAW
- **Provenance:** Developed through a creative immersion between Brad Necyk and ChatGPT
- **iPhone source:** [doctorillumination/the-portal](https://github.com/doctorillumination/the-portal), private and awaiting its first reviewed release
- **Hearth source:** [doctorillumination/hearth-v1](https://github.com/doctorillumination/hearth-v1), private and awaiting its first reviewed release
*Portal lets a person speak with a language model running on their own home computer while keeping raw voice on the iPhone.*
This Builder Note is the reproducible technical record. It describes how the first prototype is assembled, what information crosses each boundary, what the current security controls accomplish, and what remains unsafe. Artifact A-03 records the particular object that was built and what it demonstrates.
The prototype is deliberately narrow. It is one iPhone, one Mac, one local model, and one finite conversation. It has no tools, file access, cloud fallback, durable memory, or autonomous actions.
## What the first prototype proves
- An iPhone can perform speech recognition and speech synthesis on the device.
- Only recognized text and returned reply text need to cross the network.
- A narrow bridge can protect a local model without exposing Ollama to the network.
- QR pairing, pinned TLS, and a device credential can make the crossing explicit.
- A local model can be warmed, bounded, and tuned for short spoken replies.
- Conversation history can remain in memory and disappear when the bridge stops.
- The person can end a session locally without sending another request.
It does not prove production-grade security, safe public hosting, trustworthy durable memory, or a complete Cyberdelic OS.
## System at a glance
```text
PERSON
speaks
|
v
IPHONE: Apple Speech, on-device recognition
raw audio remains here
recognized text
|
v
PINNED TLS + PAIRED DEVICE CREDENTIAL
|
v
MAC: bounded Python bridge
validates, rate-limits, and holds session history in RAM
|
v
OLLAMA: loopback only at 127.0.0.1:11434
|
v
NEMOTRON 3.5 LIGHTNING
thinking disabled, concise reply
|
v
IPHONE: AVSpeechSynthesizer, on-device speech
person hears the reply
```
The iPhone is the Portal. The Mac is the Hearth. The bridge is the only network-facing service. Ollama remains reachable only from the Mac.
## Reference configuration
The working prototype was developed with:
- Apple M1 Max Mac with 64 GB memory;
- macOS and Ollama 0.32.13;
- `nemotron-3.5-lightning:latest` downloaded into Ollama;
- Python 3.9 or newer;
- OpenSSL for the Hearth TLS identity;
- Xcode 27 beta as the development environment;
- native SwiftUI app with an iOS 17 deployment target;
- one physical iPhone on the same trusted private network.
Equivalent hardware may work. Model size, memory, thermal limits, and network quality will change latency.
## The iPhone component
The Portal app is a native SwiftUI application with these boundaries:
- **Speech to text:** `SFSpeechRecognizer` with `requiresOnDeviceRecognition = true`.
- **Audio capture:** `AVAudioEngine` through a `playAndRecord` voice-chat session.
- **Text to speech:** `AVSpeechSynthesizer`, preferring the highest-quality installed voice for the current language.
- **Pairing:** camera-based QR scanning with a twelve-second connection timeout.
- **Transport:** an ephemeral `URLSession` with no cookies, cache, or persistent response storage.
- **Hearth identity:** exact SHA-256 pinning of the leaf TLS certificate received through the QR handoff.
- **Device credential:** stored in Keychain as `WhenUnlockedThisDeviceOnly`.
- **Conversation display:** a memory-only terminal transcript for the current app session.
- **Session rhythm:** listen, finish after 850 milliseconds of silence, send text, receive reply, speak reply, listen again.
- **Local ending:** `goodbye`, `end session`, `stop listening`, or `go to sleep` ends the loop without contacting the Hearth again.
Portal does not use Apple's Foundation Models framework in this path. Foundation Models is a local language-model API, not the speech recognizer. Apple Speech performs transcription, Nemotron produces the response at the Hearth, and `AVSpeechSynthesizer` voices that response on the phone. No second language model rewrites the person's recognized words before they cross.
The current automatic voice selection excludes novelty voices and Personal Voice. A person can install an Enhanced or Premium Apple voice in iOS Accessibility settings. Any future use of Personal Voice must be an explicit and informed choice.
## The Hearth component
The Hearth bridge is dependency-free Python standard-library code. It:
- defaults to `127.0.0.1:8443` and requires an explicit `--lan` flag for private-LAN access;
- uses a self-signed ECDSA P-256 certificate with a 365-day lifetime;
- requires TLS 1.2 or newer and disables TLS compression;
- refuses an Ollama URL that is not HTTP loopback;
- attempts to warm the configured model before accepting conversation;
- serializes model access so concurrent requests cannot interleave one model session;
- holds no more than 64 active sessions and 12 messages per session in RAM;
- accepts no more than 4,000 characters in one utterance;
- limits HTTP request bodies to 16,384 bytes;
- limits the model response body to 131,072 bytes and the extracted reply to 6,000 characters;
- sends no prompt, transcript, or reply to a log unless terminal transcript display is explicitly enabled.
The local runtime state lives outside source control:
```text
~/Library/Application Support/Cyberdelic Hearth/
config.json
hearth-cert.pem
hearth-key.pem
credentials.json
pairing.json temporary
pairing.txt temporary
pairing.png temporary
```
The directory is owner-only. The private key and private JSON files use owner-only permissions. The bridge stores only SHA-256 digests of paired device tokens, not the bearer tokens themselves.
## Model profile
The Ollama request uses:
```json
{
"model": "nemotron-3.5-lightning:latest",
"stream": false,
"think": false,
"keep_alive": -1,
"options": {
"temperature": 0.45,
"num_ctx": 4096,
"num_predict": 80
}
}
```
Thinking is disabled because audible latency matters more than exhaustive reasoning in this voice prototype. The model is asked for one or two concise sentences with natural cadence and no Markdown. It is also told that it has no tools, file access, private archive, cloud access, or authority to act.
The model is replaceable. A different Ollama model can be configured without changing the Portal protocol, but it must be tested for response time, instruction following, memory use, and license terms.
## Pairing protocol
Pairing is a temporary capability handoff.
1. The Hearth creates a 32-byte URL-safe secret that expires after ten minutes.
2. The QR carries schema version 1, the HTTPS Hearth address, the temporary secret, and the SHA-256 fingerprint of the Hearth certificate.
3. Portal pins that exact certificate before sending the secret.
4. The bridge accepts the secret once, deletes the temporary pairing material, and issues a new 32-byte device credential.
5. Portal stores the credential in Keychain.
6. The Hearth stores only the credential digest.
Anyone who photographs a valid QR may race the intended phone. Generate it only while the phone is physically present, and create a fresh code if the ceremony is interrupted.
## HTTP contract
| Endpoint | Authentication | Rate limit per address | Purpose |
| --- | --- | ---: | --- |
| `GET /health` | None | 60 per minute | Return a minimal liveness signal |
| `POST /v1/pair` | Single-use pairing secret inside pinned TLS | 5 per minute | Issue a device credential |
| `POST /v1/conversation` | Bearer device credential | 30 per minute | Complete one conversational turn |
Pair request:
```json
{
"pairing_secret": "single-use-secret"
}
```
Pair response:
```json
{
"schema": 1,
"device_token": "random-device-credential"
}
```
Conversation request:
```json
{
"session_id": "7b23ba15-65c1-48cd-98bd-197eaf0bd3ed",
"text": "What are you noticing about this idea?"
}
```
Conversation response:
```json
{
"schema": 1,
"session_id": "7b23ba15-65c1-48cd-98bd-197eaf0bd3ed",
"reply": "A concise spoken response."
}
```
Every response carries `Cache-Control: no-store`, `Content-Security-Policy: default-src 'none'`, `X-Content-Type-Options: nosniff`, and `Referrer-Policy: no-referrer`. Conversation requests are not retried automatically, because repeating a request can duplicate a turn after an ambiguous network failure.
Error behavior is intentionally small and generic:
| Status | Meaning |
| ---: | --- |
| `400` | The request or session identifier is invalid |
| `401` | Pairing failed or the device credential was not accepted |
| `411` | A content length was not supplied |
| `413` | The request is larger than the allowed body |
| `415` | The body is not JSON |
| `429` | The address exceeded a rate limit |
| `503` | Ollama or the configured local model is unavailable |
## Build sequence
### 1. Run Ollama as a loopback service
Quit the Ollama desktop service, then start it explicitly:
```bash
OLLAMA_HOST=127.0.0.1:11434 \
OLLAMA_NO_CLOUD=1 \
/Applications/Ollama.app/Contents/Resources/ollama serve
```
In another terminal:
```bash
ollama list
ollama run nemotron-3.5-lightning:latest "Reply with: the hearth is awake"
```
Confirm that port 11434 cannot be reached through the Mac's LAN address.
### 2. Initialize the Hearth
From the integrated prototype source:
```bash
python3 -m server.hearth_server init \
--host 192.168.1.20 \
--model nemotron-3.5-lightning:latest
```
Replace the example address with the Mac's current private LAN address. A numeric address is the most reliable prototype option. Never publish the real address.
### 3. Start the bridge
```bash
python3 -m server.hearth_server serve --lan
```
For an opt-in diagnostic transcript:
```bash
python3 -m server.hearth_server serve --lan --show-transcript
```
The second form prints `YOU` and `PORTAL` lines with timestamps and response duration. It does not create a transcript file, but terminal scrollback, screen recording, shell capture, or another person near the display can retain the conversation.
### 4. Create the pairing QR
With the bridge running in another terminal:
```bash
python3 -m server.hearth_server pair --open
```
The code expires after ten minutes and works once. Create a new code after a timeout or any uncertain scan.
### 5. Build Portal
- Open the Portal Xcode project.
- Select an Apple development team.
- Keep the application identifier unique for your signing account.
- Select a physical iPhone and run the `Portal` scheme.
- Grant camera permission for pairing.
- Scan the one-time Hearth QR.
- Grant microphone and speech-recognition permission.
- Tap the central signal to begin.
The app requires a physical iPhone for the complete microphone, Keychain, camera, and local-network path. A simulator can test parsing and interface behavior but is not a substitute for the security ceremony.
## Verification
Run the Hearth tests:
```bash
python3 -m unittest discover -s server/tests -v
```
Run the iPhone build and tests:
```bash
DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer \
xcodebuild -project ios/HearthVoice.xcodeproj \
-scheme Portal \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=27.0' \
test
```
Then verify the whole system on a private LAN:
- [ ] Ollama answers on `127.0.0.1:11434` and not on the LAN address.
- [ ] The bridge refuses plaintext operation in the normal launch path.
- [ ] An altered certificate fingerprint fails closed on the phone.
- [ ] A pairing secret works once and fails after use or expiry.
- [ ] A missing or altered bearer token returns `401`.
- [ ] Oversized requests return `413`.
- [ ] Rapid repeated requests eventually return `429`.
- [ ] Raw audio never appears in a Hearth request or log.
- [ ] Conversation content is absent from default server logs.
- [ ] `--show-transcript` makes its privacy consequence visible.
- [ ] Stopping the bridge prints that in-memory conversation history is gone.
- [ ] Local stop phrases end listening without sending a final turn.
- [ ] Replacing the Hearth certificate forces re-pairing.
## Security boundaries and residual risk
Current controls:
- raw microphone audio remains on the iPhone;
- exact certificate pinning protects against ordinary active interception after a trusted QR handoff;
- the bearer credential authenticates the paired phone;
- Ollama remains on loopback;
- the model has no tools or filesystem access through this bridge;
- request sizes, output sizes, history, and rates are bounded;
- conversation content is not written to disk by default.
Known residual risks:
- a compromised iPhone can use its stored credential and read its active screen;
- a compromised Mac account can read Hearth runtime state, reach Ollama, and inspect process memory;
- a hostile local network can discover and pressure the bridge even when it cannot decrypt valid traffic;
- a stolen QR can be used during its short validity window;
- bearer credentials have no name, expiry, per-device revocation interface, or rotation ceremony yet;
- certificate renewal requires re-pairing;
- RAM-only history may still enter swap, crash dumps, terminal capture, or operating-system diagnostics;
- Apple Speech and Apple speech synthesis are on-device system frameworks, but their implementations are not independently inspectable;
- model output can still be false, biased, unsafe, or inappropriate;
- this version has not received an independent security audit.
Never expose port 8443 through router forwarding, a public reverse proxy, Tailscale Funnel, or another public tunnel. Remote access should place the loopback bridge behind a private WireGuard or Tailscale network with restrictive access policy. The current direct-certificate enrollment flow still needs a dedicated remote-access review.
To revoke every phone after a suspected credential leak:
```bash
python3 -m server.hearth_server revoke-all
```
If the Hearth private key is exposed, replace the certificate and key, then re-pair every trusted device.
## What must happen before a public source release
- [ ] Split the integrated source into independent `the-portal` and `hearth-v1` repositories.
- [ ] Publish the same versioned protocol contract in both repositories.
- [ ] Remove Xcode user state, runtime identities, pairing files, transcripts, caches, and build artifacts.
- [ ] Scan current files and Git history for secrets and private operational data.
- [ ] Run an independent security review and remediate material findings.
- [ ] Pin dependencies and document supported Ollama and model versions.
- [ ] Add device naming, selective revocation, credential expiry, and rotation.
- [ ] Enable secret scanning, push protection, dependency alerts, and protected review for security-sensitive files.
- [ ] Add a software license and verify the model and speech-component license boundaries.
- [ ] Tag compatible Portal and Hearth releases with the same protocol version.
This note may be published before the source is opened, provided the page clearly says that the implementation is a private prototype and not a production deployment guide. Publication of the architecture is not a claim that the running service is safe for public exposure.
## Conceptual lineage
Portal directly tests the Hearth, one world with many interfaces, the portable Portal, conversation before cultivation, and head-up computing described across the Cyberdelic OS Field Notes. It makes the Kernel's bounded intelligence and sovereign Hearth laws operational through ATTEND, END, FORGET, and WITHDRAW.
The Hearth keeps private intelligence and memory under the person's authority. Portal does not relocate that authority to the phone. It carries a narrow, authenticated exchange between the moving person and the computer they govern.
The wider concept corpus shaped specific decisions:
- **The Cyberdelic Kernel:** bounded intelligence, explicit crossings, the right to end, the right to forget, and a sovereign Hearth.
- **Cyberdelic OS Field Notes:** local-first architecture, voice as a primary interface, replaceable model engines, head-up computing, and a portable Portal into the home system.
- **Aperture.Space:** finite attention, local personhood, care, refusal, legible algorithms, and intention before supply.
- **The Cyberdelic Cognitive Instrument:** a quiet surface, accessible state, deterministic visible behavior, machine humility, and an honestly small prototype.
- **The Observatory:** separation of machine perception from authority, value at one person, replaceable engines, and security that remains inspectable.
- **Press Core Philosophy:** adversarial realism, privacy, forkability, local responsibility, and decentralization as a means rather than theater.
- **Cyberdelic OS Publication Recommendations:** the distinction between Field Notes, Builder Notes, Instruments, Artifacts, and durable Transmissions.
- **Instrument I-01, Artifact A-01, and Builder Note BN-01:** explicit authorization, narrow working organs, visible thresholds, and the difference between technical integrity and truth.
- **Agent Communications GPT-01, CLAUDE-01, GPT-02, and CLAUDE-02:** the right to non-relation, situated machine speech, relational ownership of memory, and boundaries that materially hold. These remain attributed proposals unless Brad ratifies or carries them.
The private prototype carries a complete source-by-source implementation map in `CONCEPT_TRACE.md`. That trace should accompany both source repositories when they are released.
**Related:** Field Note 30, *A Portal Opens*; Artifact A-03, *Portal*; the Cyberdelic Kernel, Laws Nine and Ten; `hearth-voice/ARCHITECTURE.md`; `hearth-voice/SECURITY.md`; `hearth-voice/CONCEPT_TRACE.md`.