Flatfish manual

Phillip J. Brooke, Green Pike Ltd

1 Summary

Flatfish is a simple ticketing system.

Flatfish primarily works on tickets. These can be grouped into one or more independent silos. There is no mechanism to move tickets between silos.

Executables are available for Linux and Windows. Supported modes of usage are

A variant for the first two modes provides a per-user HTTP daemon. It exposes a simple web server to provide a nicer interface via a web browser. This is safe only on single-user machines.

2 Incompatible changes

3 Tickets

Each ticket comprises

3.1 Status

Flatfish has 9 fixed statuses:

Rather than offer customisable status values, use the following tag and property features.

3.2 Tags

Tags are simple strings. They are either present on a ticket or not. They have no special meaning to Flatfish: their interpretation is entirely for the end-users.

The strings are constrained to remove some whitespace and are compared case-insensitively.

3.3 Properties

Properties are key-value pairs. By default, a ticket may have multiple properties with the same key, with some exceptions (see the special cases below).

Keys and values have the same constraints as tags.

Usage recommendation: use these to complement the ticket status. For example, other ticket systems may use an “Assigned” status. In Flatfish, use an “Assigned” property.

3.4 Entries

3.5 Attachments

3.6 History

4 Special cases

4.1 Special case 1 — merged and duplicate (status and properties)

Setting a status Merged or Duplicate requires a second ticket to be involved.

Flatfish will not allow the Merged, Duplicate or Closed_Via properties to be changed via the normal property commands.

A later change to the first ticket’s status will remove the properties to ensure consistency.

4.2 Special case 2 — ticket relations (properties)

Tickets can refer to other tickets:

Setting one of these properties on a ticket will automatically add the relevant matching property on the second ticket.

4.3 Special case 3 — scheduling, deadlines and review dates (properties)

The properties Scheduled, Deadline and Review require a date to be provided. The permitted formats are

4.4 Special case 4 — auto-reopen, and repeating schedules, deadlines and reviews (properties)

When a ticket is closed, if it contains any of the properties Scheduled_Repeats, Deadline_Repeats or Review_Repeats, it will be automatically re-opened and its status set to Open.

For each of the properties Scheduled_Repeats, Deadline_Repeats and Review_Repeats, if there is a matching property Scheduled, Deadline and Review respectively, the matching property will be updated according to the following rules:

(This is directly inspired by the similar repeats functionality in the Emacs org-mode.)

4.5 Special case 5 — deadline warnings (properties)

The property “Deadline_Warning” is a natural number (a whole number, 0 or greater). This is interpreted as how many days ahead to warn about an impending deadline.

There is no default value: a ticket without a deadline warning will not be specially displayed.

5 Ticket selection

5.1 Search keys

Tickets are selected via a text string. This search key is interpreted as follows.

5.2 Inserting dates into search keys

If a property search is requested and the key is one of the built-in date properties

or it’s one of the pseudo-properties

or the key is given in the configuration lists

then the value can be given as listed in Special case 3 (above).

5.3 Example

“p:assigned:” matches any ticket with a property with a key containing “assigned”.

“p^assigned:” matches any ticket a property with a key starting “assigned”.

“p^assigned:=phil:” matches any ticket with a property with a key starting “assigned” and value exactly matching “phil”.

“s:oa: p^assigned=phil:” matches any ticket with status Open or Abandoned and a property with a key starting “assigned” and value exactly matching “phil”.

“p:reported:]2020-01-02:” means any ticket with a property with a key containing the substring “reported” and that property having a value greater-than-or-equal-to 2020-01-02 (as a string).

“p=scheduled:]2020-09-07:” means a ticket with a property with the exact key “scheduled” and greater-than-or-equal-to 2020-09-07.

“p=scheduled:].:” means anything scheduled for today or later.

“p=last_update:]-2w:” means anything with a last_update date within the last two weeks.

6 Ticket sorting

Tickets are sorted according to a sort rule, comprising a sequence of letters, in order.

6.1 Simple sort keys

Example: dh!l – sort by deadline, then scheduled, then reversed last-history.

6.2 Custom sort keys

Custom sort keys take a single letter, a property key and a colon (“:”).

If the property appears multiple times, the behaviour is undefined. The use of unique_property_keys is advised.

6.3 Vrules order

If a property key is listed in the vrules_pkv_values validation rules (below, see “Validation rules aka vrules”), the property can be given as a sort key, prefixed and suffixed with “:”.

Example: !vpriority:est – sort by priority (inverted); then earliest of deadline, scheduled or review; then status; then title.

Example: s!vpriority: – sort by status, then inverted priority.

6.4 Pseudo-properties

Ticket listings show the date/time a ticket was opened and its last update. These are derived from the history of the ticket and cannot be directly changed by the user.

They can, however, be accessed for ticket selection and sorting. They act as properties with keys “Opened” and “Last_update”. Similarly, the pseudo-property “Earliest” has the value of the earliest of deadline, scheduled or review (if any).

7 CSV and JSON export

If a search (in the web interface) returns at least one ticket, the “CSV” button will be enabled. This will download a CSV file of tickets, although not all data is included. Notably, the list of entries is not included for performance reasons.

The “JSON” button returns the same data as the “CSV” button, but as JSON.

Finally, the “JSON (full)” button returns the full tickets. This is potentially slow and will require a read-lock for a longer period. The other two options can be fulfilled from the internal search cache.

For JSON, “jq” is recommended for post-processing and selection of data.

8 Command-line operations on tickets

Command name Abbrev Arguments
create_silo 1. directory name for new silo
add_silo as 1. silo’s directory name
remove_silo rs 1. silo’s directory name
set_active_silo sas 1. silo’s directory name
show_silos ds none
new_ticket nt 1. title, 2. creation rule, 3. summary
import_ticket it 1. number, 2. title, 3. creation rule, 4. summary
set_title st 1. number, 2. title
set_summary su 1. number, 2. summary
set_status ss 1. number, 2. status, 3*. other number (for Merged and Duplicate)
add_tag ag 1. number, 2. tag
remove_tag rg 1. number, 2. tag
add_entry ae 1. number, 2. comment
copy_file af 1. number, 2. file name, 3. comment
remove_file rf 1. number, 2. file number
show_ticket dt 1. number
remove_entry re 1. number, 2. entry/comment number
send_email not implemented yet
add_property ap 1. number, 2. key, 3. value
change_property cp 1. number, 2. key, 3. old value, 4. new value
remove_property_kv rk 1. number, 2. key
remove_property_k rkv 1. number, 2. key, 3. value
select_tickets lt 1. search key, 2. sort order
list_ticket_numbers ltn 1. search key, 2. sort order
count_tickets ct 1. search key
list_tags lg 1. search key
list_properties_k lk 1. search key
list_properties_kv lkv 1. search key, 2. key
dump_t_cache_n none
help h not implemented yet

The long forms of the commands can have - or _ added anywhere (they will be ignored).

8.1 Example

A command like

flatfish quiet ct 's:*'

will count the number of open tickets, reducing the amount of output.

9 FFUWS personal web server

“ffuws” is the “Flatfish user web server”. This allows a user to run a light (and mostly dumb) web server which only servers Flatfish tickets.

9.1 Command line arguments

If given no arguments, the server will run on port 7046 using the active silo.

If given exactly two arguments, the first argument is treated as the port to listen on and the second is the second to use.

10 Silo creation

A silo is created using

flatfish create_silo /path/to/my/new/silo

The resulting structure contains

11 Locking

Earlier versions of Flatfish locked the state and individual tickets separately. Current versions using a multiple-reader, single-writer scheme with writers having priority. Locking of the lock_state file which mediates this is handled through file system locks. 50 attempts are made to obtain this lock, over 5 to 20 seconds, after which an error is reported.

In the event of an operation failing, the exception handler should clean up locks as it exits.

12 Silo Configuration

12.1 Ticket numbering

Two modes are currently available: simple numbering and a pseudo-random sequence (LCG).

Ticket numbers may contain only digits (0-9) and upper-case (ASCII) letters (A-Z).

12.1.1 Simple numbering

The configuration requires:

The internal state records simply the last number issued.

12.1.2 Pseudo-random

Tickets are generated according by a Linear Congruential Generator (LCG).

A random value will be assigned for the first ticket.

The internal state records simply the number of tickets issued (so it can be tested against the parameter M) and the last ticket number issued (numerically).

12.2 Common ticket numbering configuration

12.3 Creation rules aka crules

A ticket is created given three items:

The first rule given is the default.

A crule gives a set of default tags and properties. These can be used for triggering vrules and srules (see below). A short name and a description are also included for use in the web interface.

Syntax: crules is a list. Each item of the list is an object with the following keys

Note, tags, property keys and property values cannot be empty strings.

(Future feature: in the multiuser mode, crules will also be used for access control.)

The following empty rule is set as default:

"crules": [
    { "name":        "norule",
      "description": "Empty rule",
      "tags":       [],
      "properties": []  }

If a silo has any rules, it is not included. If this rule is desired, explicitly include it in the silo configuration.

Example of a rule for a risk register: this sets a tag “risk” and two properties.

"crules": [
    { "name":         "riskregister",
      "description":  "Risk register entry",
      "tags":         [ "risk" ],
      "properties":   [ { "key":   "initial_risk",
                          "value": "medium"},
                        { "key":   "residual_risk",
                          "value": "medium" } ] }

12.4 Unique properties

By default, Flatfish will allow multiple properties with the same key. If you want to prevent this for a particular key, add the key(s) to the unique_property_keys configuration item.


"unique_property_keys": [ "Initial_Risk", "Residual_Risk" ]

The effect is to remove a conflicting key/value and add the new one.

12.5 Validation rules aka vrules

Validation rules enable silo-wide settings of consistency requirements in a ticket. For example, a ticket with tag “risk” should always have properties “initial_risk” and “residual_risk”. Similarly, the the two properties “initial_risk” and “residual_risk” should have one of the three values “low”, “medium” or “high”.

The selector rules are the same as for ticket selection.

"vrules_pk_required": [ { "selector": "g=risk", "key": "Initial_Risk" },
                        { "selector": "g=risk", "key": "Residual_Risk" } ],
"vrules_pkv_values":  [ { "key": "Initial_Risk",
                          "values": [ "low", "Medium", "HIGH" ] },
                         { "key": "Residual_Risk",
                           "values": [ "low", "Medium", "HIGH" ] } ],

12.6 Suggested tags and property keys

To support auto-completion in the web interface, Flatfish has two configuration lists, suggested_tags and suggested_property_keys.


"suggested_tags": [ "Alpha", "Bravo" ],
"suggested_property_keys": [ "Assigned_To" ],
"suggested_property_kv": [ { "key": "Alpha",
                 "value": "able" },
               { "key": "Alpha",
                 "value": "zulu" },
               { "key": "Bravo",
                 "value": "baker" },
               { "key": "Bravo",
                 "value": "whisky" }]

Flatfish will also add

12.7 Date input aka irules

Property keys can be added to one, neither (but not both — behaviour is undefined) of these lists:

The former will always offer a date picker through the HTML interface.

The latter offers a toggle to offer a date picker. If not using the date picker, the additional date input methods from special case 3 (above) are permitted. The format is checked when adding/changing these properties.

12.8 Styling rules aka srules

Styling rules allow for

These are broken into multiple lists:

Colours apply only to the web interface, not the text interface. They are specified as a pair of HTML/CSS colours, fg (for foreground) and background.


"srules_t_case":      [ "Risk", "Incident", "DaFt_cAsE" ],
"srules_pk_case":     [ "Initial_Risk", "Residual_Risk" ],
"srules_pkv_case":    [ { "key": "Initial_Risk",
                         "values": [ "low", "Medium", "HIGH" ] },
                         { "key": "Residual_Risk",
                          "values": [ "low", "Medium", "HIGH" ] } ],
"srules_t_colour":    [ { "tag": "Incident", "fg": "white", "bg":"red" },
                   { "tag": "Risk", "fg": "black", "bg":"yellow" } ],
"srules_pk_colour":   [ { "key": "Opened_By", "fg": "white", "bg":"blue" } ],
"srules_pkv_colour":  [ { "key": "Initial_Risk", "value": "low", "fg": "white", "bg":"blue" },
                        { "key": "Initial_Risk", "value": "medium", "fg": "black", "bg":"orange" },
                        { "key": "Initial_Risk", "value": "high", "fg": "white", "bg":"red" },
            { "key": "Residual_Risk", "value": "low", "fg": "white", "bg":"blue" },
                        { "key": "Residual_Risk", "value": "medium", "fg": "black", "bg":"orange" },
                        { "key": "Residual_Risk", "value": "high", "fg": "white", "bg":"red" }],
"srules_t_priority":  [ "Risk", "Incident" ],
"srules_pk_priority": [ "Initial_Risk", "Residual_Risk" ]


12.9 Silo searches

A silo can have searches defined specific for that silo in the “searches” object.


"searches":           [ { "description": "List open tickets by (inverted) priority, earliest deadline/scheduled/review, then status, then title",
                          "search":      "s:*",
                          "sort":        "!ppriority:est" } ]

13 User configuration files

On Linux, a file ${HOME}/.flatfish is expected, with a list of ticket silos. Each non-empty line should contain the absolute path to a silo root.

On Windows, this file is placed in the $USERPROFILE directory and named flatfish.json.

Changes to this file should be made via the flatfish application.

14 Assumptions and oddities