Skip to content

Access Condition System (ACS)

ENiGMA½ uses an Access Condition System (ACS) that is both familiar to oldschool BBS operators and has its own style. With ACS, SysOps are able to control access to various areas of the system based on conditions such as group membership, connection type, terminal capabilities, and more. Various touch points in the system are configured to allow for acs checks. In some cases ACS is a simple boolean check while others (via ACS blocks) allow defining what conditions must be true for certain rights such as read and write (though others exist as well).


ENiGMA½ does not utilize legacy “security levels” (see note below) but instead uses a group system. Users may belong to one or more groups which can be checked by the GM ACS code (see ACS Codes below). Two special groups exist out of the box:

  1. users: Any regular user.
  2. sysops: System Operators. The first user (your root/admin) will always belong to this group.

You do not need to explicitly create groups: by checking for them via ACS and adding members to a group, they implicitly exist within the system. You may use as many groups as you like. See oputil user group for adding and removing users to groups.


ACS strings are one or more ACS codes combined with logical operators. Whitespace (spaces) between operators, codes, and parentheses is optional — you can write compact strings like GM[users]|NC5 or readable ones like GM[users] | NC5.

OperatorNameDescription
&ANDBoth sides must be true. This is also the default — two adjacent codes with no operator are implicitly AND’d.
|OREither side must be true.
!NOTNegates the following check.
( )GroupingControl evaluation order.

From highest to lowest:

  1. ! (NOT) — binds tightest, applies to the immediately following code or group
  2. Implicit AND / & — adjacent codes or explicit &
  3. | (OR) — loosest binding

Parentheses override precedence: (A | B) & C requires either A or B to be true, AND C to be true.

ACS codes take an optional argument immediately after the two-letter code:

FormDescriptionExamples
numberA single integer valueNC5, TH24, AA30
[values]A comma-separated list of values in bracketsGM[users,sysops], ID[1,42], WD[0,6]
(none)Some codes take no argumentSC, LC

Spaces are allowed around commas inside lists: GM[users, sysops] is valid.

ACS StringMeaning
GM[users]User belongs to the users group
NC2User has called at least 2 times
ID1User is ID 1 (the SysOp)
GM[elite,power]User belongs to elite or power group
ID1 | GM[co-op]User is the SysOp OR belongs to co-op
!TH24Terminal height is NOT exactly 24 (i.e., 24 fails; 25+ passes since TH checks >=)
GM[users] & SCUser is in users group AND connection is secure
GM[users] NC5Same as above but with implicit AND — user is in group AND has 5+ calls
(GM[sysops] | ID1) & SCSysOp or ID 1, AND secure connection required
!GM[banned]User is NOT in the banned group
GM[users] & !GM[restricted]In users but not in restricted

The following ACS codes are available:

CodeCondition
IDn, ID[n,…]User’s ID is n or one of [n,…]
GM[group,…]User belongs to one of [group,…]
AGageUser’s age is >= age years
ASstatus, AS[status,…]User’s account status is status or one of [status,…]. 0=inactive, 1=active.
AFfactorUser’s current authentication factor is >= factor. Factor 1 = password/pubkey, factor 2 = 2FA (OTP).
ARfactorUser requires authentication factor >= factor. 1=always true, 2=true only if user has 2FA configured.
PV[name,value]User property name is exactly value. Allows arbitrary property checks, e.g. PV[message_conf,local].
CodeCondition
NCcallsUser’s login/call count is >= calls
NPpostsUser’s message post count is >= posts
AAdaysUser’s account is >= days old
UPcountUser’s upload file count is >= count
DLcountUser’s download file count is >= count
BUbytesUser’s total uploaded bytes is >= bytes
BDbytesUser’s total downloaded bytes is >= bytes
NRratioUser’s upload/download count ratio is >= ratio%
KRratioUser’s upload/download byte ratio is >= ratio%
PCratioUser’s post/call ratio is >= ratio%
CodeCondition
ACcountUser’s total achievement count is >= count
APpointsUser’s total achievement points is >= points
CodeCondition
LCConnection is local
SCConnection is secure (SSL/TLS, secure WebSocket, etc.)
ECencodingTerminal encoding: 0 = CP437, 1 = UTF-8
THheightTerminal height is >= height
TWwidthTerminal width is >= width
TT[type,…]Terminal type is one of [type,…] (ansi, xterm, etc.)
TM[theme,…]User’s current theme ID is one of [theme,…] (e.g. luciano_blocktronics)
NNnode, NN[node,…]Current node number is node or one of [node,…]
CodeCondition
WDday, WD[day,…]Day of week is day or one of [day,…]. 0=Sunday, 1=Monday, …, 6=Saturday.
MMminutesCurrent time is >= minutes past midnight (system time)
CodeCondition
SE[service,…]All listed services are enabled. Service names are case-insensitive. Available: http, https, web (either http or https), gopher, nntp, nntps, activitypub (requires web), nodeinfo2 (requires web), webfinger (requires web). Unknown service names always fail.
AEenabledActivityPub is enabled for the current user: 1=yes, 0=no

Some areas of the system require more than a single ACS string. In these situations an ACS block is used to allow finer-grained control. Each key in the block names a right (read, write, download, etc.) and maps to an ACS string.

acs: {
read: GM[users]
write: GM[sysops,co-ops]
download: GM[elite] | UP10
}

All users can read (see) the area, sysops and co-ops can write (upload), and only members of elite or those with 10+ uploads can download.

When an ACS block is not specified (or a particular scope is missing), the system applies sensible defaults:

ContextScopeDefault
Message ConferencereadGM[users]
Message ConferencewriteGM[users]
Message AreareadGM[users]
Message AreawriteGM[users]
File AreareadGM[users]
File AreawriteGM[sysops]
File AreadownloadGM[users]
FSE Body UploaduploadAcsGM[users]
Menu Moduleacs(no check — all users can access)

The following areas of the system support ACS checks:

See the specific area documentation for details on available scopes and defaults.


Some configuration fields (such as next in menus) support conditional arrays where each element may include an acs check. The first matching condition is used:

next: [
{
acs: GM[sysops]
next: sysopMainMenu
}
{
acs: GM[users]
next: userMainMenu
}
{
// No acs — fallback for everyone else
next: guestMenu
}
]