Amici di Penna is a free, not-for-profit pen-pal community for families with children aged 2 to 17. The community had outgrown WhatsApp and spreadsheets, so we designed and built its bilingual public website and private family platform. Parents register, pass manual review, create child profiles, browse limited summaries, agree connections and message one another without putting children's names, contact details or postal addresses into discovery. We also set up the hardened Vultr production stack and automated releases.
From WhatsApp and spreadsheets to its own platform
Amici di Penna began as a family-run pen-pal group and grew to more than 160 families around the world. By then, WhatsApp and spreadsheets were carrying member details, introductions and matching work they were never meant to handle. The new platform needed to preserve the warmth of the community while giving parents a clear place to join, find a possible match and manage the exchange. We designed the visual identity and public website around handwritten letters, family and the link between Italy and Australia, then carried the same experience into the account area.
Parents lead every step
Children write the letters, but adults run the connection. A parent registers, verifies their email, accepts the current policies and waits for manual approval. Once approved, they can create a child profile, browse suitable summaries from other families, send a connection request and message the other parent after both sides agree. The public journey, account screens and emails work in English and Italian, including validation and recovery paths rather than only the visible marketing pages.
Child safety changed the data model
A standard member directory would have been the wrong shape for this project. Child profiles are private and owned by the parent account. Discovery shows an age band, country, languages, interests and pen-pal group, but not a child's name, date of birth, school, town, contact details or postal address. Connection requests, messages, reports, blocking and account deletion all enforce ownership and permissions in the application, not just in the interface. Public comments and XML-RPC are disabled, and private routes send no-index and no-cache headers.
Hosting was part of the build
We set up the production environment on Vultr rather than handing over a repository and leaving the risky work for later. Caddy manages HTTPS in front of Nginx, PHP-FPM and MySQL, with the application services kept on private Docker networks. Firewall rules expose only SSH, HTTP and HTTPS; SSH is key-only and rate-limited, direct root login is disabled, and Fail2ban and unattended security updates are enabled. Deployments run from GitHub Actions through a pinned server identity, create revisioned releases and switch the live path only after the application responds successfully.
Build notes
- A free international community of more than 160 families, split between an Italian group and a wider global group for children aged 2 to 17.
- A custom Bedrock WordPress build using Timber, Twig and ACF, with the family data and application rules kept in a dedicated plugin rather than tied to the public theme.
- Parent-owned child profiles that expose only an age band, country, languages, interests and group during discovery. Names, dates of birth, email addresses, phone numbers and postal addresses stay out of search results.
- Email verification, current policy acceptance and manual account review before a parent can enter the member area, plus private connection requests, reporting, blocking and permanent account deletion.
- English and Italian across the public pages, account journeys, validation messages, metadata and lifecycle emails.
- A production Docker stack on Vultr with Caddy-managed HTTPS, Nginx, PHP-FPM and MySQL on private service networks, owner-only secrets and security headers at the edge.
- Server rules limited inbound traffic to SSH, HTTP and HTTPS. Password login and direct root access were disabled, with a non-root deploy account, Fail2ban, rate limiting and unattended security updates in place.
- GitHub Actions packages and deploys the current main revision over pinned SSH, activates it only after health checks, and leaves the database, uploads and certificate state persistent between releases.
- Release testing covered 141 isolated browser journeys and 119 PHP tests with 1,184 assertions, backed by static analysis and dependency audits.


