Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
630379d
Convert database to JSON document storage
z9security Feb 4, 2026
9a72d9c
Add support for explicit ID assignment
z9security Feb 4, 2026
3cfac62
Add migration tool for upgrading existing databases
z9security Feb 4, 2026
759247f
Use version 100 for JSON schema, detect old schema on startup
z9security Feb 4, 2026
213b0de
Add Z9/Open Community protocol service with loopback tests
z9security Feb 5, 2026
08b1cf7
Map Z9/Open Community Cred to Aporta credentials and persons
z9security Feb 5, 2026
8d03de6
Flip connection direction so panel connects outbound to host
z9security Feb 5, 2026
42affa2
Make log file path configurable via appsettings.json
z9security Feb 5, 2026
7a92dc8
Fix Z9/Open Community protocol handshake and identification
z9security Feb 5, 2026
bba3139
Add OSDP over TCP/IP support and Z9 Dev message processing
z9security Feb 5, 2026
a7b5e9d
Add CRED_READER_ONLINE/OFFLINE event support for Z9 Open Community
z9security Feb 5, 2026
312ed54
Fix OSDP online status notification for Z9 integration test
z9security Feb 6, 2026
b8fbc57
Add access event support and fix TCP endpoint ID parsing
z9security Feb 6, 2026
db1b8f6
Use BinaryFormatter for credential encoding/decoding
z9security Feb 6, 2026
b517f97
Persist all Z9 Open data types to SQLite database
z9security Feb 6, 2026
974ceb5
Implement access granted flow for Z9 Open Community protocol
z9security Feb 6, 2026
5632b03
Implement privilege checking for Z9 Open Community access control
z9security Feb 6, 2026
a30d17b
Update Z9.Protobuf.Community to 1.0.5
z9security Feb 6, 2026
1a58bd0
Add credential status checks for access denied scenarios
z9security Feb 6, 2026
46c9926
Update Z9.Protobuf.Community to 1.0.6 and add door-specific privilege…
z9security Feb 6, 2026
27fd4fd
Fix migration test to expect current schema version 102
z9security Feb 6, 2026
77e85ce
Update Z9.Protobuf.Community to 1.0.7 and use privUnid for privilege …
z9security Feb 6, 2026
cc28bc7
Add Z9 Open Community integration roadmap to README
z9security Feb 6, 2026
ebbbff9
Wire door strike output from Z9 actuator model and handle DevActionReq
z9security Feb 6, 2026
b69ee95
Report door state events (unlocked/locked/opened/closed) to Z9
z9security Feb 7, 2026
b54033c
Update Z9 integration roadmap with completed items and new TODOs
z9security Feb 7, 2026
4ec04d8
Add REX (Request to Exit) support with activateStrikeOnRex door config
z9security Feb 7, 2026
8129775
Use strikeTime from Z9 DoorConfig for momentary unlock duration
z9security Feb 7, 2026
f2c359b
Detect door forced open when door opens without strike active
z9security Feb 7, 2026
73ca1ce
Support extended strike time from credential door access modifiers
z9security Feb 7, 2026
ee25200
Detect door held open and send DoorHeld/DoorNotHeld events
z9security Feb 7, 2026
d1e4622
Update README with Z9/Open Community Profile integration features
z9security Feb 7, 2026
63dac35
Support door mode: unlocked, locked, and card-only with DevAction han…
z9security Feb 7, 2026
05bdc22
Update README with door mode support features
z9security Feb 7, 2026
5399081
Match credentials by decoded card number in Z9/Open Community mode
z9security Feb 7, 2026
0304987
Require credential template for Z9/Open Community access decisions
z9security Feb 7, 2026
2bffb69
Fix EvtSubCode for missing credential template denial
z9security Feb 7, 2026
15ef9c2
Add standalone/primary mode with Z9 data structures and --primaryConf…
z9security Feb 7, 2026
60356db
Use manual enrollment flow instead of auto-enrollment in standalone mode
z9security Feb 8, 2026
9b6367e
Merge person identity into Z9 Cred, eliminate credential_assignment
z9security Feb 8, 2026
c3b66b9
Unify classic Aporta events with Z9/Open Evt protos
z9security Feb 8, 2026
02b2d9e
Restructure README with Completed/TODO sections for Aporta and Z9/Open
z9security Feb 8, 2026
512815b
Enforce element-level schedule restrictions on DoorAccessPrivElements
z9security Feb 8, 2026
7b0922e
Wire holiday support into schedule evaluation
z9security Feb 8, 2026
3621058
Add PIN support: keypad buffering, confirming PIN validation, unique …
z9security Feb 8, 2026
5ccf86e
Add Z9/Flex Community REST API with session auth and CRUD endpoints
z9security Feb 8, 2026
15f8636
Consolidate flex tables into main migration, change default password …
z9security Feb 8, 2026
576c411
Migrate Monitoring page events to Flex API
z9security Feb 8, 2026
3f29737
Unify Aporta UI with Z9/Flex API for credentials, sensors, and actuators
z9security Feb 8, 2026
ad0faac
Consolidate door/input/output tables onto z9_dev and switch ExternalI…
z9security Feb 9, 2026
d52ee25
Generate RAW_CRED_READ events on unknown reads instead of auto-enrolling
z9security Feb 9, 2026
067d8b3
Remove person and credential_assignment tables and PersonRepository
z9security Feb 9, 2026
9c96b66
Remove LastEvent from credential table and all references
z9security Feb 9, 2026
9929089
Eliminate credential table — unify on z9_cred with denormalized cred_num
z9security Feb 9, 2026
302cc3f
Update Z9.Protobuf.Community to 1.0.11
z9security Feb 9, 2026
4337ec2
Eliminate endpoint table — unify on z9_dev with IO_CONTROLLER_EXTERNAL
z9security Feb 9, 2026
becb6d4
Use Dev.Enabled for pool/assigned distinction, regenerate community p…
z9security Feb 9, 2026
05ca790
Add DevStateService for centralized in-memory device state tracking
z9security Feb 9, 2026
cc29907
Remove Events/Credentials/People legacy code, migrate Credentials UI …
z9security Feb 9, 2026
4daa372
Migrate Sensors, Actuators, and Monitoring pages to Flex API
z9security Feb 9, 2026
024cfa6
Migrate Doors.razor to Flex API, delete DoorCalls legacy code
z9security Feb 9, 2026
8d1cf47
Delete DoorsController — integration tests now use Flex API
z9security Feb 9, 2026
7b7d37d
Document API architecture in README
z9security Feb 9, 2026
c6e959d
Add Kiota FlexClient integration test
z9security Feb 10, 2026
e5447a7
Enable DevStateRecord transmission over Z9 Open Community protocol
z9security Feb 10, 2026
1fa75b4
Add CONTROLLER_STARTUP event on startup
z9security Feb 10, 2026
7ca197b
Add MOMENTARY_UNLOCK event for host-initiated momentary unlocks
z9security Feb 10, 2026
7cdabc5
Remove /flex/ prefix from API routes to align with swagger spec
z9security Feb 10, 2026
b33818a
Add OSDP LocalStatus handling for tamper and power cycle events
z9security Feb 10, 2026
b726d88
Add SCHED_ACTIVE / SCHED_INACTIVE events with per-minute schedule eva…
z9security Feb 10, 2026
8ea0741
Clarify CONTROLLER_ONLINE/OFFLINE TODO as subcontroller-scoped
z9security Feb 10, 2026
a9e27d4
Update TODO: clarify power/battery event prerequisite
z9security Feb 10, 2026
d34b852
Populate event refs, add Card Read label, and enroll from raw reads
z9security Feb 10, 2026
b822b4e
Use Messages from Z9.Protobuf.Community for event display strings
z9security Feb 10, 2026
1af335e
Remove timestamps from screenshot filenames and update markdown title
z9security Feb 10, 2026
089de52
Add login screen to Aporta web UI
z9security Feb 10, 2026
224a114
Add door access management UI with schedule restrictions and server-s…
z9security Feb 11, 2026
55e542d
Add card format management page to web UI
z9security Feb 11, 2026
fa00412
Add holiday management page and holidays toggle on schedule elements
z9security Feb 11, 2026
0359564
Add nuget.config to enforce nuget.org as sole package source
z9security Feb 22, 2026
8a6cd16
Revert "Add nuget.config to enforce nuget.org as sole package source"
z9security Feb 22, 2026
ff61a20
Switch Z9.Flex.Community from local ProjectReference to NuGet package
z9security Feb 22, 2026
45c81b1
Update Z9.Protobuf.Community to 1.0.15
z9security Mar 20, 2026
7894526
Update Z9.Protobuf.Community to 1.0.16
z9security Mar 20, 2026
f6bf1f9
Update Z9.Protobuf.Community to 1.0.17
z9security Mar 20, 2026
2976043
Update Z9.Protobuf.Community to 1.0.18
z9security Mar 20, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -350,3 +350,6 @@ MigrationBackup/
.ionide/

.idea/

# Selenium screenshots
screenshots/
113 changes: 93 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,108 @@

[![Build Status](https://dev.azure.com/jonathanhorvath/Aporta/_apis/build/status%2Fbytedreamer.Aporta?branchName=develop)](https://dev.azure.com/jonathanhorvath/Aporta/_build/latest?definitionId=2&branchName=develop)

Aporta is an open source physical access controller. The main purpose of the software is to secure doors from unauthorized access. This is accomplished by determining if a person presenting an issued credential is authorized to enter a door. The door is unlocked momentarily by the software if the person has been given access.
Aporta is an open source physical access controller. The main purpose of the software is to secure doors from unauthorized access. This is accomplished by determining if a presented credential is authorized to enter a door. The door is unlocked momentarily by the software if the credential has the appropriate access rights.

Aporta doesn't intend to recreate what is already available by existing physical access controllers. The goal is to provide a level of flexibility that is not possible with existing closed physical access control systems. Below are the design considerations that are going to make Aporta truly versatile.

- The software is written to be platform independent. This gives Aporta the ability to run on wide range of hardware. The same code runs on Windows, Mac, or Linux, even lower power devices such as Raspberry Pi.
- Modularity is built into the architecture of the software. There will be the ability to add custom plugins for enhanced capabilities.
- Open standards are embraced and built into the software.
- Open standards are embraced and built into the software — most notably OSDP, and Z9/Open Community Profile.
- Features are not to be restricted by licensing. All the software's capabilities are available without being overburden with expensive and confusing license terms.

## Release Plan ##
## Z9/Open Community Profile ##

The project is early in its development. After working on access control products for many years, my inclination is that there are a large number of features required for a viable access controller. The list below is an attempt to limit the feature targeted in the first release.
Aporta uses the Z9/Open Community Profile data model for data storage, as well as host communications. The protocol and library are here: https://github.com/z9security/z9open-community. This profile covers important core electronic door access control functionality: credentials, access levels, schedules, holidays, card formats, events, status, door modes, and device actions such as momentary unlock.

- Easy installation **(Done)**
- Windows installer **(Done)**
- Debian packages **(Done)**
## Z9/Flex Community Profile ##

Aporta supports the Z9/Flex Community Profile as a JSON/REST API, intended for use by the built-in web user interface. This API is not generally used for data synchronization with other systems, as Z9/Open Community Profile is much better suited for high-performance data synchronization. The protocol and library are here: https://github.com/z9security/z9flex-community. Z9/Open Community Profile and Z9/Flex Community Profile are essentially the same data model with 2 different representations (Google Protobuf, and JSON/REST, respectively).

## Completed ##

### Aporta
- Easy installation
- Windows installer
- Debian packages
- Self hosted web management
- SSL required by default **(Done)**
- A master password to gain access
- SSL required by default
- OSDP Driver
- Install new devices with security channel encryption
- Process standard Wiegand card reads
- Detect if device is online
- Control output
- Access Control
- Enroll new credential
- Read entire card data for card number
- Log access events
- Door access privilege management from Credentials page (assign/remove doors, set schedule restrictions)
- Door Access column on Credentials page showing assigned doors with schedule names
- Card format management (create, edit, delete binary card formats with parity, field, and static elements)
- Holiday management (create, edit, delete holidays with repeat and schedule preservation options)

### Z9/Open Community Profile
- Connection & Protocol
- Outbound TCP connection from panel to host (panel-initiates model)
- Protobuf-based Z9/Open Community protocol with identification handshake
- Automatic configuration download on connect (DbChange messages)
- Device Management
- OSDP credential readers over TCP/IP, configured from Dev messages
- Door model with strike actuator, door contact sensor, and REX sensor — all wired automatically from the device hierarchy
- Credential reader online/offline status reporting
- Device status reporting to host via DevStatus messages
- Credential & Data Format Support
- Credential mapping from Cred objects (card number, facility code, enabled/disabled, effective/expiry dates)
- Credentials stored by card number; card reads decoded using DataFormat definitions to extract and match by card number
- Card format definitions (DataFormat, DataLayout, DataGroup) for decoding raw card data into meaningful fields
- Credential templates, data layouts, and data formats persisted to local SQLite database
- Access Control Decisions
- Privilege-based access control using DoorAccessPriv and CredPrivBinding
- Door-specific privilege checking (precision access and privilege group elements)
- Element-level schedule restrictions on DoorAccessPrivElements
- Schedule enforcement with SchedRestriction (time-of-day, day-of-week)
- Holiday-aware schedule evaluation
- Credential status checks: disabled, not yet effective, expired
- Access granted and denied events reported to host with appropriate EvtSubCode
- Door Control
- Door strike actuation on access grant with configurable strike time from DoorConfig
- Extended strike time support via credential DoorAccessModifiers (extDoorTime flag)
- Request to Exit (REX) with configurable activateStrikeOnRex
- Door momentary unlock via DevActionReq from host
- Door mode support: unlocked, locked, card-only, card+confirming PIN, unique PIN only, card-only or unique PIN
- PIN support: keypad entry buffering, confirming PIN validation, unique PIN lookup
- Default door mode applied from DoorConfig on door creation
- Runtime door mode change via DevActionReq (DoorModeChange) from host, including reset-to-default
- REX behavior respects current door mode
- Door Alarm Monitoring
- Door state reporting: unlocked, locked, opened, closed
- Door forced open detection with DoorForced/DoorNotForced events
- Door held open detection with DoorHeld/DoorNotHeld events
- Extended held time support via credential extDoorTime flag
- Events
- CONTROLLER_STARTUP on service start
- MOMENTARY_UNLOCK audit trail when host initiates a momentary unlock action
- TAMPER / TAMPER_NORMAL events with dev state tracking when OSDP reader reports tamper
- CRED_READER_POWER_CYCLE event when OSDP reader reports power cycle
- RAW_CRED_READ event in primary mode for credential enrollment
- SCHED_ACTIVE / SCHED_INACTIVE events with per-minute schedule evaluation

## API Architecture ##

The web UI communicates with the server through the Z9/Flex Community Profile API (e.g., `/cred/list`, `/door/list`). The Extensions and Driver Configuration pages still use the original REST API (`/api/Extensions`), which is maintained as-is for now.

## TODO ##

### Aporta
- Self hosted web management
- Configurable user accounts (currently uses default credentials)
- OSDP Driver
- Install new devices with security channel encryption **(Done)**
- Process standard Wiegand card reads **(Done)**
- Detect if device is online **(Done)**
- Control output **(Done)**
- Notify when input is tripped
- Access Control
- Enroll new cardholder **(Done)**
- Basic access level assignment
- Read entire card data for card number **(Done)**
- Card number is a non-reversible hash **(Done)**
- Log access events **(Done)**

### Z9/Open Community Profile
- DbChange: Handle device deletions (devDelete, devDeleteAll) — currently silently ignored, causing stale devices to accumulate
- EvtControl: Implement event flow control (StartContinuous, ConsumeUpTo) for generic delivery of events persisted while offline
- Events: CONTROLLER_ONLINE / CONTROLLER_OFFLINE — for IO_CONTROLLER_SUB subcontrollers (requires subcontroller device model first)
- Events: Power/battery (POWER_PRIMARY/OFF_PRIMARY/NONE, BATTERY_OK/LOW/FAIL/CRITICAL) — needs a way of expressing that an input/sensor is used for this purpose

## Installation ##

Expand Down Expand Up @@ -70,7 +141,7 @@ The log files will be placed in the standard Linux logging directory

### Quick Start

After installing Aporta, browse to the local web url ```https://localhost:8443``` to get started. A [Quick Start Guide](https://github.com/bytedreamer/Aporta/wiki/Quick-start-guide) can be found in the Wiki section.
After installing Aporta, browse to the local web url ```https://localhost:8443``` to get started. You will be presented with a login screen. The default credentials are username ```admin``` and password ```pass```. A [Quick Start Guide](https://github.com/bytedreamer/Aporta/wiki/Quick-start-guide) can be found in the Wiki section.

### Configuration ###

Expand Down Expand Up @@ -106,3 +177,5 @@ General information about configuring and setting up the Aporta project for deve

[A summary of all of the Core Abstractions and Concepts](https://github.com/bytedreamer/Aporta/wiki/Aporta-Core-Abstractions-and-Concepts) used in the Aporta code.

Note: The Wiki documents the pre-Z9/Open Community Profile architecture.

1 change: 1 addition & 0 deletions src/Aporta.Core/Aporta.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Z9.Protobuf.Community" Version="1.0.18" />
</ItemGroup>

<ItemGroup>
Expand Down
Binary file removed src/Aporta.Core/DataAccess/Aporta.sqlite
Binary file not shown.
118 changes: 103 additions & 15 deletions src/Aporta.Core/DataAccess/Migrations/_0000_InitialCreate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,113 @@ namespace Aporta.Core.DataAccess.Migrations
{
public class _0000_InitialCreate : IMigration
{
public int Version => 0;

public string Name => "Initial create";
// Version 100 to distinguish from old column-based schema (versions 0-10)
public int Version => 100;

public string Name => "Initial create with JSON document storage";

public async Task PerformUpdate(IDbConnection connection, IDbTransaction transaction)
{
await connection.ExecuteAsync(
@"create table schema_info
(
id integer not null
constraint schema_info_pk
primary key,
name text not null,
timestamp datetime not null
);

create unique index schema_info_id_uindex
on schema_info (id);",
@"
-- Schema info table (unchanged)
CREATE TABLE schema_info (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
timestamp DATETIME NOT NULL
);
CREATE UNIQUE INDEX schema_info_id_uindex ON schema_info (id);

-- Extension table (GUID primary key)
CREATE TABLE extension (
id TEXT NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);
CREATE UNIQUE INDEX extension_id_uindex ON extension (id);

-- Global setting table (string primary key)
CREATE TABLE global_setting (
name TEXT NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Credential Template table
CREATE TABLE cred_template (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Data Layout table
CREATE TABLE data_layout (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Data Format table
CREATE TABLE data_format (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Privilege table
CREATE TABLE priv (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Schedule table
CREATE TABLE sched (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Holiday table
CREATE TABLE hol (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Holiday Calendar table
CREATE TABLE hol_cal (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Holiday Type table
CREATE TABLE hol_type (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Z9 Device table (stores full Dev proto messages)
CREATE TABLE z9_dev (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL,
external_dev_mod_id TEXT
);

-- Encryption Key table
CREATE TABLE encryption_key (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL
);

-- Z9 Credential table (stores full proto including privBindings)
CREATE TABLE z9_cred (
id INTEGER NOT NULL PRIMARY KEY,
data TEXT NOT NULL,
cred_num TEXT
);
CREATE UNIQUE INDEX z9_cred_cred_num_uindex ON z9_cred(cred_num) WHERE cred_num IS NOT NULL;

-- Z9 Event table
CREATE TABLE z9_evt (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
);

",
transaction: transaction);
}
}
}
}
31 changes: 0 additions & 31 deletions src/Aporta.Core/DataAccess/Migrations/_0001_AddExtensionTable.cs

This file was deleted.

34 changes: 0 additions & 34 deletions src/Aporta.Core/DataAccess/Migrations/_0002_AddEndpointTable.cs

This file was deleted.

33 changes: 0 additions & 33 deletions src/Aporta.Core/DataAccess/Migrations/_0003_AddOutputTable.cs

This file was deleted.

Loading