Open-source Contao 5 bundle for managing community resources and controlling access to shared spaces.
Developed for the Zukunftsforum Rissen and designed for reuse in similar community environments.
Many community spaces rely on mechanical keys.
Keys are hard to manage, easy to duplicate, and difficult to revoke.
This bundle provides a secure digital alternative using a pull-based device architecture:
- devices never accept inbound connections
- all communication is initiated by the device
- reduced attack surface
| Mode | Description |
|---|---|
| live | real hardware devices |
| emulator | emulator devices for testing |
Devices are distinguished by a single flag:
isEmulator = false→ real device (Raspberry Pi)isEmulator = true→ emulator device
All door openings follow the same flow:
Member
→ API
→ OpenDoorService
→ DoorGatewayResolver
→ DoorGateway
→ Device
There is no direct shortcut and no alternative execution path.
| Channel | Target |
|---|---|
| physical | real hardware |
| emulator | emulator device |
| Mode | Allowed Devices |
|---|---|
| live | real devices only |
| emulator | emulator devices only |
- door access control API
- device polling endpoint
- door job workflow
- Contao backend integration
- permission-based access
- audit logging
- device monitoring
- browser-based demo UI
Deploy only in trusted environments.
Main endpoints:
POST /api/door/open/{area}
GET /api/device/poll
POST /api/device/confirm
GET /api/door/whoami
Devices authenticate via dedicated device API users.
The emulator behaves like a real device:
- polls the API
- receives jobs
- confirms execution
This allows full workflow testing without hardware.
Der Modus emulator ist ein temporärer Test- und Übergangsmodus für Umgebungen ohne reale Türöffner-Hardware.
Die Verarbeitung erfolgt cronbasiert über einen geschützten Emulator-Endpunkt. Ein Cronlauf führt für bis zu ca. 55 Sekunden wiederholt Emulator-Ticks aus, sodass offene Jobs zeitnah verarbeitet werden können. Dennoch kann die Ausführung gegenüber dem Live-Modus verzögert sein.
Der Emulator-Modus ist nicht als dauerhafte Produktionsbetriebsart vorgesehen.
The demo is a pure UI feature and not part of the system logic.
Characteristics:
- no device communication
- no polling
- no confirm
- no door jobs
It is intended for:
- presentations
- explaining the workflow
The system uses a pull-based architecture:
Backend → DoorJob queue → Device polls → Job dispatch → Confirm
Workflow:
- member triggers door open
- job created (pending)
- device polls
- job dispatched
- device confirms
- job finalized (executed / failed / expired)
Dispatch is atomic to prevent race conditions.
Typical installation:
Contao Server → Raspberry Pi → Relay (e.g. Shelly) → Door
Install via Composer:
composer require zukunftsforum-rissen/community-offers-bundle
Run migrations:
vendor/bin/contao-console contao:migrate
Open the demo:
/demo
Add required environment variables to .env.local.
COMMUNITY_OFFERS_MODE=live
COMMUNITY_OFFERS_MAIL_FROM=noreply@example.org
COMMUNITY_OFFERS_MAIL_REPLY_TO=info@example.org
COMMUNITY_OFFERS_APP_URL=https://example.org/app
Defines the available areas.
COMMUNITY_OFFERS_AREAS='[ {"slug":"workshop","title":"Workshop"}, {"slug":"sharing","title":"Sharing"} ]'
Maps areas to Contao group IDs.
COMMUNITY_OFFERS_AREA_GROUPS='{ "workshop": 3, "sharing": 5 }'
COMMUNITY_OFFERS_DOI_TTL=172800
COMMUNITY_OFFERS_PASSWORD_TTL=86400
COMMUNITY_OFFERS_CONFIRM_WINDOW=30
COMMUNITY_OFFERS_LOGIN_IDENTIFIER=email
ENABLE_LOGGING=true
ENABLE_DEBUG_LOGGING=false
The bundle supports different login identifiers.
Typical values:
username
email
Recommended:
If the login identifier is set to:
COMMUNITY_OFFERS_LOGIN_IDENTIFIER=email
it is recommended to extend the Contao login template.
This improves:
- field labeling
- usability
- clarity for users
Create a custom login template:
templates/frontend_module/mod_login_email.html.twig
Content:
{% extends "@CommunityOffers/frontend_module/mod_login_dynamic_username.html.twig" %}
{#
Dieses Template erweitert das Standard-Login-Template
und überschreibt nur den Username-Block.
#}In the Contao backend:
Login module → Template:
mod_login_email
Even when using email as login identifier:
- the internal username remains stable
- the email address can still be changed
- login remains consistent
Example:
COMMUNITY_OFFERS_MODE=live
COMMUNITY_OFFERS_LOGIN_IDENTIFIER=email
COMMUNITY_OFFERS_MAIL_FROM=noreply@example.org
COMMUNITY_OFFERS_MAIL_REPLY_TO=info@example.org
COMMUNITY_OFFERS_INFO_ADDRESS=info@example.org
COMMUNITY_OFFERS_APP_URL=https://example.org/app
COMMUNITY_OFFERS_RESET_PASSWORD_URL=https://example.org/reset-password
COMMUNITY_OFFERS_AREAS='[
{"slug":"workshop","title":"Workshop"},
{"slug":"sharing","title":"Sharing"},
{"slug":"depot","title":"Depot"},
{"slug":"swap-house","title":"Swap House"}
]'
COMMUNITY_OFFERS_AREA_GROUPS='{
"workshop": 3,
"sharing": 5,
"depot": 7,
"swap-house": 9
}'
COMMUNITY_OFFERS_DOI_TTL=172800
COMMUNITY_OFFERS_PASSWORD_TTL=86400
COMMUNITY_OFFERS_CONFIRM_WINDOW=30
ENABLE_LOGGING=true
ENABLE_DEBUG_LOGGING=false
The system supports two fundamentally different access request workflows.
These workflows must remain clearly separated in both implementation and documentation.
Used when:
A person does not yet have a member account.
Typical flow:
- User fills access request form
- Access request is stored
- DOI email is sent
- User confirms email address
- Member account is created
- User sets password
- Admin approves request
Result:
A new member (tl_member) is created.
Key characteristics:
- Requires form input
- Requires password setup
- Creates new member
- Login disabled until admin approval
Used when:
A member already exists.
Typical flow:
- Member clicks request inside the app
- DOI email is sent
- Member confirms email
- Admin approves request
- Additional permissions/groups assigned
Result:
Existing member receives additional permissions.
Key characteristics:
- No form required
- No password step
- No new member created
- Only permissions/groups are modified
These workflows differ in:
- Member lifecycle handling
- Security behavior
- Database writes
- Permission management
- Audit logging
Mixing these workflows may cause:
- Duplicate members
- Incorrect permissions
- Broken login flows
- Inconsistent audit history
You must provide at least one gateway that supports live mode.
Example:
services:
App\Door\ShellyDoorGateway: tags: - { name: community_offers.door_gateway }
ZukunftsforumRissen\CommunityOffersBundle\Door\DoorGatewayInterface: alias: App\Door\ShellyDoorGateway
The DoorGatewayResolver selects the appropriate gateway based on runtime mode.
Optional via environment variables:
ENABLE_LOGGING=true
ENABLE_DEBUG_LOGGING=true
Logs:
var/logs/community-offers.log
Run checks:
composer ci
Includes:
- coding standards
- static analysis
- unit tests
See docs/:
- architecture
- api
- development
- operations
- security
MIT
See:
docs/frontend/app-pwa.md