Contact Form
Overview
The contact form is a custom PHP implementation in the child theme functions.php. It captures name, email, subject, and message; validates via Turnstile + Reoon; stores to protected CSV; and notifies via Zammad ticket, Telegram, and email.
Shortcode
[id86_contact_form]
Rendered on the Contact page (WP-CLI created, page ID 4217, slug contact-us).
Production URL: https://id86.net/contact-us/
The page uses a minimal, light, article-width layout matching the blog content column. The form keeps a white card surface with a subtle border and shadow, responsive spacing, and the collaboration-focused helper text.
Submission Flow
Security Layers
| Layer | Mechanism |
|---|---|
| Honeypot | Hidden id86_con_hp field catches automated bots |
| Nonce | WordPress CSRF via id86_contact_nonce |
| Turnstile | Cloudflare bot detection (id86_verify_turnstile_token) |
| Reoon | API verification requires is_safe_to_send = true (power mode) |
| Input sanitization | sanitize_text_field, sanitize_email, sanitize_textarea_field |
| CSV locking | flock(LOCK_EX) prevents concurrent write corruption |
Storage
Directory: /home/Dmg59ZFtKg6bIws1/id86net/id86-contact-submissions/ (outside public_html, inside the PHP open_basedir boundary)
| File | Schema |
|---|---|
messages.csv | timestamp,ip,name,email,subject,message |
failed-verifications.csv | timestamp,ip,name,email,subject,message,verification_status,is_safe_to_send,verification_mode,overall_score,error |
Notifications
| Channel | Destination | Trigger |
|---|---|---|
| Zammad | Ticket in id86.net group (customer auto-created via guess:email) | Reoon-verified submissions only |
| Telegram | Shared Support_notification group — message prefixed ID86 Contact to distinguish from opshell | Reoon-verified submissions only |
donnyaw@gmail.com (option id86_contact_notification_to) via SMTP no-reply@id86.net | Reoon-verified submissions only |
The customer-facing Zammad article contains the submitted name, email, subject, and message. It excludes the visitor IP address and Reoon verification metadata; those remain available in protected storage and internal notifications.
WordPress Options
| Option | Purpose |
|---|---|
id86_turnstile_site_key / id86_turnstile_secret_key | Dedicated Cloudflare Turnstile widget for id86.net |
id86_zammad_url / id86_zammad_api_token / id86_zammad_group | Zammad REST API ticket creation |
id86_contact_telegram_bot_token / id86_contact_telegram_chat_id | Telegram notification |
id86_contact_notification_to | Owner notification email |
Function Reference
| Function | Purpose |
|---|---|
id86_verify_turnstile_token(token) | Server-side Turnstile verification |
id86_contact_submissions_dir() | Create/get submission directory |
id86_save_contact_message_csv(...) | Append to messages.csv |
id86_save_failed_contact_verification_csv(...) | Log failed Reoon verifications |
id86_contact_notification_body(...) | HTML email body |
id86_send_contact_notification_email(...) | SMTP notification to owner |
id86_send_contact_zammad(...) | POST /api/v1/tickets with customer_id: "guess:email" |
id86_send_contact_telegram(...) | Bot API sendMessage (HTML parse mode, ID86 Contact prefix) |
id86_render_contact_form() | Shortcode handler |
Production Verification
The complete flow has been tested in production:
- Cloudflare Turnstile accepts the dedicated
id86.netwidget. - Reoon power verification accepts deliverable addresses and records the verification result internally.
- Valid submissions append to
messages.csv. - Zammad creates tickets in the
id86.netgroup with the visitor as the customer. - Telegram receives an internal ID86 Contact notification.
- Owner email notifications are delivered.
- Zammad replies are delivered to Gmail from
support@mail.id86.netafter SPF and DKIM configuration. - Customer-facing quoted content includes name, email, subject, and message only; IP and Reoon metadata remain internal.
Zammad Integration
Zammad runs on GSM16 (same instance as opshell). The id86.net group (id 3) uses:
- API token
id86-contact-form(agent,ticket.agentpermission only) - Inbound/outbound email address
support@mail.id86.netvia its own Email::Account channel (id 5)
Agent replies to customers are sent as support@mail.id86.net using the email reply composer. See opshell Zammad docs for the shared architecture.
The initial article is created as an email article with the visitor email as from and support@mail.id86.net as to. Zammad links the customer with customer_id: "guess:email". In the current Zammad UI, the agent may still need to enter the customer email manually in the reply To field; this is a composer behavior, not a missing customer record. Enable Email - full quote in Composer Settings to include the original form details in replies.
The root domain remains on MXroute for normal mail. The dedicated mail.id86.net subdomain is routed to the Docker Mail Server on GSM16 for Zammad mail. Its SPF, DKIM, and DMARC records authorize the Zammad sender and prevent Gmail rejection.