Skip to content

Package Format

This is the reference for package.yaml, the file that describes a SharpMUSH package. For what packages are and how to install them, start with Softcode Packages. Package Declarations covers the roles, settings, wiki pages, attribute entries and other blocks a package brings beside its objects, and Application, Plugin and UI Packages the other kinds of package.

A package is a folder with a package.yaml in it. The file describes the result you want, not a script: there is no ordering, no @create lines, and no dbrefs. Objects are named with {{ref}} tokens, which the installer turns into real dbrefs when the package is applied. The server compares that description with the live game, a wizard reviews the changes, and only then is anything written. Upgrades merge with your local edits, every apply is recorded, and an install can be rolled back.

Every top-level key a softcode package can use, with the format version that added it:

format: "1.10" # format version (omit = 1; 1.2 adds roles and permissions, 1.3 settings, 1.4 wiki pages, 1.5 attribute entries, 1.6 the engine range, 1.7 plugins, 1.8 attribute indexes, 1.9 attribute watches, 1.10 category)
package: myrddins-bbs # required: lowercase letters, digits and hyphens, up to 64 characters
version: 2.4.1 # required: a version number (see Versions below)
authors: [Myrddin] # optional: a list, or one string
description: "Bulletin Board" # optional
license: MIT # optional: an SPDX licence name is recommended
homepage: https://example.com # optional
keywords: [bbs, boards] # optional: up to 5, used for browsing and searching
category: Community # optional (1.10): the group the setup wizard lists it under (see Category)
convention_prefix: BBS_ # optional: the attribute name prefix you use; advisory, not enforced
requires_server: ">=2.0" # optional: the lowest SharpMUSH version it runs on
verified_server: "2.4" # optional: the newest SharpMUSH version it was tested on (1.6)
maximum_server: "2" # optional, only for a known break: the newest it runs on (1.6)
plugins: [scene] # optional (1.7): plugins its code calls, by plugin id (see Plugins a package calls)
depends: # optional: other packages this one needs
- volund-core: ">=1.0" # short form: id and version range
- some-util # just an id: any released version
- http-handler: ">=1.0" # provides the GET router bbs_routes adds a route to
- package: who-where # full form: adds where to find it (see Source hints)
version: ">=1.0"
source:
repo: https://github.com/SharpMUSH/SharpMUSH-Packages
path: who-where/
branch: main
conflicts: # optional: can't be installed alongside these
- legacy-bbs: "<2.0"
replaces: myrddins-board # optional: the id this package used to have
configure: # optional: questions the installer is asked
bbs_storage:
label: "Object that stores posts"
type: dbref # dbref (default) | string | number | boolean
board_name:
label: "Board display name"
type: string
default: "Community Board" # defaults are allowed for every type except dbref
objects: # required: at least one object
- ref: bbs_global # required: a name for this object, unique in the package (lowercase)
type: thing # required: thing | room | exit | player
name: BBS Global Object # required: its name in the game
parent: "{{bbs_parent}}" # optional: one ref
location: "{{?bbs_storage}}" # optional for things; required for exits
previous_refs: [bbs_main] # optional: names this object had before (for renames)
flags: [no_command] # optional: object flags
powers: [Send_OOB] # optional: object powers
locks: # optional: locks by lock type
use: "{{bbs_parent}}"
attributes: # optional: attributes to set
CMD_+BBREAD: # full form: a value and attribute flags
value: |-
$+bbread *:@pemit %#=[u({{bbs_parent}}/FN_READ,%0)]
flags: []
FN_HEADER: "[repeat(=,78)]" # short form: quoted one-liners only
DATA_TITLE: |-
{{?board_name}}
- ref: bbs_parent
type: thing
name: BBS Parent Object
- ref: bbs_routes # an attach entry: manages attributes on an object it didn't create
target: "{{$http_handler}}"
attributes:
GET`BBS: |-
@respond/type application/json; think [u({{bbs_parent}}/FN_JSON)]
categories: # optional (1.2): categories for the roles and permissions below
roles:
- name: Boards
description: Roles for the people who run the boards.
permissions:
- name: Boards
description: Moderating the bulletin boards.
permissions: # optional (1.2): custom permissions the softcode checks
- name: bbs.moderate
category: Boards # declared above, or already in the game
description: Edit or remove anyone's post.
roles: # optional (1.2): roles that set custom permissions only
- slug: bbs-moderator
name: Board Moderator
category: Boards
color: "#4fc3c8"
priority: 11
permissions:
bbs.moderate: allow
settings: # optional (1.3): configuration options, as @config/set takes them
noisy_whisper: "yes"
attributes: # optional (1.5): attribute entries, as @attribute makes them
BBS`READ:
flags: [no_command, wizard]
index: true # optional (1.8): an index, as @attribute/index makes it

The sections below cover the manifest itself. The blocks from categories: on are in Package Declarations, and kind:, application: and binaries: in Application, Plugin and UI Packages.

format: names the version of this format the manifest is written for. A server warns about a newer minor version and ignores the keys it doesn’t know; it refuses a newer major version. This server reads format 1.10.

FormatAdds
1The manifest on this page
1.1Application packages
1.2categories:, permissions: and roles:
1.3settings:
1.4wiki: and locale:
1.5attributes:
1.6verified_server and maximum_server
1.7plugins:
1.8An attribute entry’s index:
1.9An attribute entry’s watch:
1.10category:

Write "1.10" in quotes, so YAML tools don’t read it as 1.1.

Write every softcode value as a block scalar (|-). A block scalar keeps backslashes, brackets, $patterns, % substitutions and #dbrefs exactly as written. Single quotes are fine for trivial one-liners (double any ' inside). Never use unquoted or double-quoted values for code. Indent block scalars with spaces (tabs are a YAML error), and use |- unless you want a newline at the end.

The escaping rules are softcode’s own, not the package format’s: write %[ %] %( %) for literal brackets and parentheses. [[]x[]] is not an escape for anything, and an empty [] is refused, sometimes without saying so. Two places this catches people in a package:

  • A bare ( inside header(...), footer(...) or line(...) ends the call early, so the title is cut short and the ) appears after the line.
  • A +help topic is evaluated before it is shown, so +help/list [<source>] loses its brackets. Write %[<source>%] or \[<source>\].
SyntaxKindWhat it points at
{{name}}internalAnother object in this package
{{pkg/name}}cross-packageAn object from package pkg, which must be listed under depends:
{{$name}}well-knownA server setting: room_zero, master_room, player_start, god, package_manager, http_handler, event_handler (a server may add more)
{{?name}}configureThe installer’s answer to a configure: question

Refs go in parent:, location: and destination: (exactly one token, in quotes, because a bare { confuses YAML), and anywhere inside attribute and lock values.

What installed code looks like. Installed code never contains raw dbrefs. In attribute values, each token becomes [v(PM`REFS`NAME)], and the installer keeps a PM`REFS`NAME attribute on every object whose code uses that ref, holding the object it points at (or the configure answer). Refs to another package are grouped under its id (PM`REFS`WHO-WHERE`WW_FUNCTIONS). That keeps installed code readable and lets an admin point a ref somewhere else by editing one attribute; that local change survives upgrades. parent:, location:, destination: and lock strings are not evaluated as code, so they get the dbref directly.

The PM` attribute tree is reserved: a package may not define attributes under it. Ref names must also be unique across kinds within a package, so an object storage and a configure question storage would clash.

These rules are checked when the manifest is read:

  • Ref names ignore case ({{BBS_PARENT}} is {{bbs_parent}}); write them in lowercase.
  • Every {{...}} token must be a valid ref that resolves. A malformed token, an internal ref to an object that isn’t in the package, an unknown well-known name, a {{?ref}} with no configure: entry, or a {{pkg/...}} ref to a package not in depends: is an error.
  • To write a literal {{ in code, double it: {{{{ gives {{. Ordinary softcode brace groups such as @switch x={{a},{b}} are fine as they are; only exactly-double braces around a simple name are read as refs.
  • Only dbref configure answers can be used in parent:, location: and destination:.
  • Exits need location: (the room they leave from) and destination:.
  • Rooms may not have location:, and only exits have destination:.
  • Things and players may have location:; without one they start in the Package Manager wizard’s inventory.

flags: and powers: are lists of names, as @set and @power take them; locks: maps a lock type to its key. An object that needs a power for its work declares it, rather than leaving an administrator to grant it by hand. plus-help’s librarian, for example, reads HELP trees on objects it doesn’t own and writes command links. Here it is with a flag and a lock added to show those keys too:

objects:
- ref: librarian
type: thing
name: Help Librarian
location: "{{$master_room}}"
flags: [no_command]
powers: [See_All, Send_OOB]
locks:
use: "=me"

Flags, powers and attribute flags merge on upgrade the way attributes do. One the new version adds is set, one it drops is cleared, and one an administrator cleared by hand stays cleared while the package still declares it. Flags and powers the package never declared are left alone.

Managing attributes on an object you didn’t create

Section titled “Managing attributes on an object you didn’t create”

Some packages need to manage softcode on a core object they don’t own, such as the HTTP handler or the master room. An entry with target: is an attach entry: the package manages only the attributes it lists, and never creates, changes or destroys the object.

objects:
- ref: handler
target: "{{$http_handler}}" # an existing object to manage attributes on
attributes:
GET: |-
think routed
  • Only ref, target and attributes are allowed: no type, name, parent, location, destination, flags or locks.
  • The target must already exist. It can be a {{$well_known}} object, a {{?configure}} answer, or {{dependency/ref}}: an object another package provides, which must be listed under depends:. A ref to this same package is not allowed; put those attributes on the object directly.
  • The package records the attributes it manages (so upgrades merge them and uninstalling removes them), but not the object, so uninstalling leaves it in place.
  • A package can’t be uninstalled while another is attached to it. If package B attaches to an object package A created, A can’t be uninstalled until B is removed (or the uninstall is forced). http-handler and profile-handler are the standard example: profile-handler adds its routes under http-handler’s attributes and depends on it.

Versions are MAJOR[.MINOR[.PATCH]][-prerelease]: 2.4.1, 1.0, 3.0.0-beta.1. Prerelease ordering follows SemVer 2.0.0 exactly: alpha < alpha.1 < alpha.beta < beta < beta.2 < beta.11 < rc.1 < release. Build metadata (+...) is not supported.

Ranges use >=, >, <=, <, = and bare versions (an exact match). Clauses separated by spaces or commas must all hold. ^1.2 and ~1.2 are not supported; write >=1.2, and add <2.0 only once you know 2.0 breaks your package (Versioning Packages).

Prereleases are never picked unless you ask for one. >=1.0 does not match 2.0.0-beta. A prerelease only matches when a clause names a prerelease of the same major.minor.patch (>=1.2.3-alpha matches 1.2.3-beta but not 1.3.0-beta).

A package can’t depend on itself, ids can’t repeat, and an id can’t be in both depends: and conflicts:. provides:, recommends: and suggests: are reserved for a future format version.

A dependency can say where to find it, so a missing dependency becomes “requires who-where >=1.0, fetch it from this repository?” instead of a dead end. Use the full form (a package: key marks it, which is also why package can’t be a dependency’s id):

depends:
- package: who-where
version: ">=1.0" # optional; leave out for any released version
source: # optional
repo: https://github.com/SharpMUSH/SharpMUSH-Packages
path: who-where/ # optional: the package's folder in a repository of several
branch: main # optional: defaults to the repository's default branch

source: can also be just a repository address. The hint is only advice: the installer shows it (with the usual warning for repositories that aren’t configured remotes), and the admin decides whether to fetch it. It never overrides a remote already configured for the same package.

For objects, list their old ref names in previous_refs:. An upgrade then treats the change as a rename (same dbref) instead of destroying and recreating the object, which would break everything pointing at the old dbref. For packages, replaces: old-id carries the install history across a package rename.

The manifest is checked as a whole, and every problem is reported with its exact place (for example objects[2].attributes.FN_READ). SharpMUSH.PackageTool runs the same checks on your own machine.

Errors (the manifest is refused): a missing or invalid package, version or objects; invalid ids, ref names or object types; repeated refs, attributes or dependency ids; any invalid or unresolvable {{token}}; parent loops; exits without a location or destination (or a location on a room, or a destination on anything but an exit); configure type mistakes (a default for a dbref, a non-dbref answer in a placement field); a package depending on itself, or the same id in both depends: and conflicts:; an invalid kind; an application package without an application: block or with objects: (and the reverse for a softcode package); an invalid application slug, type, role or zone; a format version whose major number is newer than the server supports; an upper bound or exact version in requires_server, a verified_server that is not a version, a maximum_server that is not one to three numbers, or a maximum_server below requires_server. In the format 1.2 and 1.3 blocks: an invalid category, permission or role name; a built-in permission or a system role; a role priority outside 1-29 or a colour that isn’t #rrggbb; a role permission other than allow or deny; a configuration option that doesn’t exist or that a package can’t set, a value it doesn’t take, or a ref for an option that isn’t a dbref; any of these blocks in a plugin or UI package. In format 1.4: a page that isn’t namespace:slug in the main, help or category namespace, a page listed twice, a page with both or neither of file and markdown, or a link that doesn’t name its page in full. In the format 1.5 attributes: block: a name @attribute would refuse, a flag that isn’t an attribute flag, a limit that doesn’t compile, both a limit and an enum, an empty enum, a delimiter longer than one character or a choice that contains it, a retroactive other than true or false, or the block in a manifest whose format is below 1.5. In format 1.7: a plugins: entry that isn’t a valid plugin id. In format 1.8: an index: that isn’t true, false or the options @attribute/index takes, or one on an entry with internal in its flags. In format 1.9: a watch: that isn’t true, false or the attribute wildcards @attribute/watch takes. In format 1.10: a category: longer than 40 characters.

Warnings (the manifest is accepted): unknown keys (likely typos); reserved keys (provides, recommends, suggests, configure pattern); configure questions nothing uses; more than 5 keywords; a format version whose minor number is newer than the server supports; a verified_server below requires_server or above maximum_server; a plugin listed twice in plugins:; an index: in a manifest whose format is below 1.8, which is ignored; a watch: in a manifest whose format is below 1.9, which is ignored; a category: in a manifest whose format is below 1.10, which is ignored; a permission or role naming a category the package doesn’t declare, or a role allowing a permission the package doesn’t declare (the game must already have it, or the install stops).

Repositories, release tags and the packages built into the server are covered in Softcode Packages.

Installed objects belong to the Package Manager wizard (#7, set by the package_manager option). The game records which package created each object and manages each attribute, including each attribute’s value as installed. That is what makes upgrades a real three-way merge: the original, your live edits and the new version. Every apply is recorded with the manifest, your configure answers and the previous values of everything it changed, so an install or upgrade can be rolled back, and upgrades never ask configure questions you have already answered.

These examples are checked against the format by SharpMUSH’s own tests, so they always match what the installer accepts. They are published in the official package repository, SharpMUSH-Packages, which you can add as a remote under Admin → Packages → Remotes.

PackageWhat it shows
who-whereSeveral objects, internal refs, parents, flags and attribute trees (CMD`WHO, FUN`WHERE`<SWITCH>)
plus-helpObject powers (See_All, Send_OOB), and the librarian other packages attach their +help topics to
sceneA custom permission (scene.close) placed in the game’s own Staff category
http-handler, profile-handlerAttach entries on the HTTP handler, and one package attaching to another
scene-help, scene-configBridge packages: two attach entries joining scene to plus-help, and one joining it to plus-config (see Bridge Packages)
scene-verseChanging another package’s output without editing it: scene details and +scene filters attached to the Scene Logger (see Scene Details)
chargen + chargen-appA portal application: the softcode routes in one package, the application registered by another, with a configurable role
jobs + jobs-appA complete system: commands added with @command/add and @hook/override/inline, a role and permission category, eight permissions and eight roles declared by the package, per-object hooks, +help topics, and a portal application driven by the same code
messagesA configuration option (messages_object) pointed at the package’s own object (format 1.3)
bboardsAn attribute entry (BBOARD, wizard and no_command by default) declared in the manifest (format 1.5)
hello-uiA plugin package: a compiled plugin that adds a portal page
builder-graphA UI package: a hosted HTML and JavaScript page

Four smaller packages, written for learning the format, are in the SharpMUSH Guide repository under Examples/Packages/. None of them ships with the server.

PackageWhat it shows
hello-worldThe smallest package: one object, one command, and a +help topic
starter-areaRooms and exits (location: and destination:)
bbs-liteDependencies with source hints, typed configure questions, cross-package refs, conflicts, locks and prerelease versions
coast-guideA content pack: wiki pages only, one of them pinned to the wiki home (format 1.4)

hello-world, slightly shortened:

format: 1
package: hello-world
version: 1.0.2
authors: [SharpMUSH]
description: "Minimal example package: one object, one command."
license: MIT
keywords: [example, greeting]
depends:
- package: plus-help
version: ">=1.0"
objects:
- ref: hello_global
type: thing
name: Hello World Object
attributes:
CMD`HELLO: |-
$+hello:@pemit %#=Hello, [name(%#)]!
# +help in its smallest form: a HELP root describing the set, one leaf per topic,
# and one attach entry telling the +help librarian where to look.
- ref: hello_help
type: thing
name: Hello Help
attributes:
HELP: |-
The example package.
HELP`HELLO: |-
`+hello` says hello. That is the entire package.
- ref: hello_help_registration
target: "{{plus-help/librarian}}"
attributes:
SRC`HELLO-WORLD: |-
{{hello_help}}

After installing it, players get a +hello command that greets them by name.

  • Write substitutions bare: %0, %q<name>. Brackets are for function calls only.
  • Prefer firstof() to nested if() calls for “first one that works” choices.
  • Name attributes as trees, <kind>`<section>`<Name>: CMD`BBPOST, FUN`DISPLAY`LIST, DATA`CONFIG`<NAME>.
  • Keep function objects no_command, and put $command patterns on a separate global object (see who-where).
  • Never hardcode a dbref. Use {{?configure}} for objects that differ between games and {{$well_known}} for ones every game has.
  • Quote fields that are only a ref (parent: "{{x}}"); a bare leading { is YAML syntax.