A key is either a string (any text, quoted) or a bare name.

A name is a letter, followed by letters and digits, optionally separated by single underscores. It never starts or ends with an underscore and never contains two in a row:
name = [A-Za-z] ( _? [A-Za-z0-9] )*
The "letter" here is an ASCII letter — lowercase or uppercase:


| Valid bare names | Must be quoted instead |
|---|---|
host, maxRetries, a_1_b2, A, x9 | _private, name_, a__b, 1st, my key, $foo |
Narrower than JSON5 — on purpose
JSON5's bare-key grammar follows the full ECMA-262 IdentifierName production, so JSON5 additionally accepts bare keys like $foo, _x, or Unicode identifier characters. FSON does not widen name to match. This is the one place a JSON5 file may need a key quoted to also be valid FSON. See FSON and JSON5.
Round-trip
The library remembers whether each key was written quoted or bare and writes it back the same way. FsonWriter::setPreferBareKeys(true) (and fson-check --bare) rewrites quoted keys without quotes wherever name allows it — an opt-in normalisation, off by default so the author's style is preserved. Key::isValidName() is the public check; Key::bare() throws on an invalid name.

