Paclets Overview

Paclets are units of Wolfram functionality, packaged up in a way that allows them to be discovered, installed, updated and integrated seamlessly into the Wolfram environment. Paclets can contain many types of content, including Wolfram Language files, LibraryLink libraries, front end resources like palettes and stylesheets, Java libraries for use with J/Link, documentation notebooks, etc. The essential element that makes a paclet is the PacletInfo.wl file, a small, simple file of metadata that describes the paclet, its requirements and the ways in which it extends the Wolfram environment.
When in use, a paclet consists of a directory containing a PacletInfo.wl file along with one or more content files and subdirectories. A paclet can be compressed into a single .paclet file, which is a convenient form for distributing and installing, but it is not necessary for a paclet to be packed into a .paclet file.
Paclets are used internally by the Wolfram system to break the product into components and to provide updates and bug fixes outside of the normal product release cycle. The Mathematica 13 layout includes over 270 paclets.
The paclet system is documented and exposed to users, so you can create and distribute your own products and packages. Wolfram is also creating a Paclet Repository that will accept user submissions, hoping to create a vibrant ecosystem of easily discoverable and accessible paclets.
The PacletInfo.wl File
A PacletInfo.wl file is a simple file of metadata in Wolfram Language format that describes a paclet and the resources it provides. It is the presence of a PacletInfo.wl file that makes a directory or collection of files into a paclet. You can also name this file PacletInfo.m if you prefer.
Here is a PacletInfo.wl file for a very simple new paclet called FileUtilities. This paclet is just a single implementation file, FileUtilities.wl, which defines the "FileUtilities`" context containing a few useful functions. Users of the paclet will load the package in their session in the usual way, using Needs[FileUtilities`].
PacletObject[<|
"Name" "FileUtilities",
"Version" "1.0",
"Extensions" {
{"Kernel", "Root" "Kernel", "Context" "FileUtilities`"}
}
|>]
The "Name" and "Version" fields are required in all paclets and are the only required fields (although a paclet with only "Name" and "Version" would not be very useful). The "Extensions" field is where you describe what types of resources your paclet providesthe ways in which your paclet extends the system. A paclet that contains .wl or .m files that define packages to be loaded with Get or Needs should specify a "Kernel" extension and list the context(s) the paclet provides. In the preceding example, the Wolfram System knows that when a user executes Needs["FileUtilities`"], this paclet provides the .wl file that corresponds to that context.
Every paclet has a version number, and although the paclet system is designed to support side-by-side installations of multiple versions of a paclet, by default it uses the one with the highest version number. If you are releasing paclets to be used by others, make sure that you increment the version number with every release, otherwise your new version will not be recognized as being different from the old version.
You can put comments in Wolfram Language syntax into PacletInfo.wl files.
The essence of paclet functionality is that you declare the content your paclet provides via the PacletInfo.wl file, and when the paclet is installed, those types of content will be found automatically by the system. Users generally do not have to do anything beyond installing your paclet. Paclets "weave" themselves into the system so that their resources can be found. Palettes show up in the menu, documentation shows up in the Help Viewer, contexts can be loaded with Needs, etc.
You might be aware that the Wolfram Engine looks for many types of resources based on the value of $Path. The paclet system has a much more powerful lookup mechanism that makes manual modifications to $Path a thing of the past. When the Wolfram Engine looks up a resource, $Path is still used, but the paclet system is always consulted first, falling through to $Path only if no paclet provides the resource.
Changes beginning in Version 12.1: A significant change to the PacletInfo.wl file format was made in Version 12.1. Formerly, the head of the expression was Paclet, not PacletObject, the contents were not wrapped in an Association, and the left-hand sides of rules were typically symbols (although they could have been strings). Files in the old format are still supported, but discouraged for new paclet development. Here is what the previous PacletInfo.wl file looks like in the old format: Paclet[ Name "FileUtilities", Version "1.0", Extensions { {"Kernel", Root "Kernel", Context "FileUtilities`"} } ]
PacletInfo Fields
The PacletInfo.wl file supports a number of fields. Fields are always in the form "FieldName" -> value, where value is typically a string or list of simple elements like rules, lists and strings. You cannot put arbitrary Wolfram Language expressions into field values; these files are not evaluated when read into the kernel, so you cannot put something in that evaluates to the value that you wantthe file must contain the literal value.
The "Name" field is required. This is the name by which your paclet will be known. It is used in functions like PacletInstall and PacletFind. You can use any characters you like in a paclet name, but it is not recommended to use a hyphen character (-), so it is conventional to use a single underscore (_) as a word separator if desired. The paclet name has no significance other than as a unique and meaningful identifier. For example, it does not need to match the name of a context or file that your paclet provides, although this will typically be the case.
The "Version" field is required. The version is a string, not a number (i.e. "1.0", not 1.0). The version can have up to five digit blocks ("1.0.0.0.1234"), and the version comparison is exactly as you would expect ("1.10" is greater than "1.9"). You cannot include non-numeric characters like "a", although this feature will likely be provided in the future.
Use this field to specify what versions of the Wolfram Language your paclet is compatible with. If you leave this field out, it is assumed that you are compatible with all versions. If your paclets "WolframVersion" specification does not match the currently running instance, your paclet will be invisible in that session. You can use *, +, - and comma characters in this specification, as follows:
"13+"
All versions from 13.0.0 onward
"13.1.0-"
All versions 13.1.0 and earlier
"13.*"Only Version 13 (and any minor version like 13.1,etc.)
"13"
Only Version 13.xx (same as "13.*")
"12.3,13.0"Only Versions 12.3 and 13.0
WolframVersion compatibility specifications.
Use this field to specify what operating systems your paclet is compatible with. If you leave this field out, it is assumed that it is compatible with all systems. If your paclets "SystemID" specification does not match the currently running instance, your paclet will be invisible in that session. The value can be a single string like "MacOSX-x86-64" or a list like {"Windows-x86-64", "Linux-x86-64"}.
Paclets with a "Kernel" extension can specify how their contexts should be loaded. The default is Manual, meaning that users need to call Get or Needs on your contexts to load them, but you can specify "Startup" to have your contexts loaded every time the kernel starts up, or Automatic to specify that your contexts should be autoloaded the first time one of your exported symbols is used. The details of this system are described later on. Most developers will leave this out and rely on the default Manual loading behavior.
If you specify Updating->Automatic, your paclet will autoupdate itself the first time it is used in a session. If no update is available, this check is very fast and does not touch the network. Note that autoupdating only works for paclets with a "Kernel" extension, as the moment when updating is performed is when a context supplied by the paclet is first asked to be loaded. For paclets with other types of extensions, like "FrontEnd" or "Documentation", update checking is not performed before resources from the paclet are used.
The "Root" field specifies where the paclets contents are located relative to the paclets location. The paclet's location is defined to be the directory in which the PacletInfo.wl file resides. The default value for "Root" is ".", meaning that the paclets contents should be looked for in the same directory that contains the PacletInfo.wl file, but if you prefer a different file organization, for example where the PacletInfo.wl file sits outside the main content directory, you can use the "Root" field to point to that content directory. Most developers will not use this field. Do not confuse the paclet "Root", which is being discussed here and is very rarely specified, with the "Root" field of individual extensions, which will be discussed later and is important and commonly used.
The "Qualifier" field is used in complex situations where you have one paclet that should be broken into multiple paclets, typically for different operating systems. For example, you might have a paclet that includes a large executable or a large LibraryLink library, and there will be a different one for each SystemID. You could make a single paclet that contained subdirectories for each SystemID, but this might become very large, having copies of each binary for each system. You do not want your users to have to download tens of megabytes of libraries for operating systems they are not using. Instead, you can split your paclet up into paclets "qualified" by the SystemID for which they are intended. Details of this scenario are described later on.
These fields have no effect on the behavior of an installed paclet, but they can be used to provide information to users about what the paclet is for, who created it, and where they can go for further information and support.
You can invent any field you like, as long as the name is a string and the value is a string or list of simple elements like strings, rules and lists. Custom fields will not be directly used by the paclet system, but you can extract their values using Wolfram Language code.
Paclet Extensions
A paclet can extend the Wolfram environment in many different ways. Your paclets "Extensions" field tells the paclet system the ways in which your paclet extends the Wolfram System.
The paclet system is fundamentally declarativeyou declare, via the PacletInfo.wl file, what elements your paclet provides, and the paclet system makes sure those elements can be found through normal system operation. In a sense, paclets are passive things; you do not need to "load" a paclet into your session. You simply install it once, and from then on it weaves itself into the system so that the resources it provides can be found automatically.
When the paclet system looks for a resource specified by an extension, it creates a full path based on the following components:
paclet location / paclet root / extension root / resource path
As discussed earlier, your paclet can have top-level "WolframVersion" and "SystemID" fields that specify its compatibility. Those two fields can also appear in extensions, to control which extensions are visible in particular systems. For example, if you have different versions of your .wl files depending on what SystemID is running, you can have multiple "Kernel" extensions that have different "SystemID" fields and different "Root" fields pointing at a different subdirectory for each system.
You can specify more than one type of each extension, with different properties like "Root", "SystemID", "Language", and "WolframVersion".
Extension Types

Kernel

Probably the most common resource a paclet can provide is a Wolfram Language package (a .wl or .m file or set of those files) that can be loaded with Needs or Get. This is specified via the "Kernel" extension.
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"Kernel", "Root" "Kernel", "Context" {"MyPaclet`"}},
(* ... other extensions *)
}
|>]
With a PacletInfo.wl file like this, this is the layout that your paclet would be expected to have:

1.gif

It is an intended but not required convention of paclets to have their .wl and .m files in a Kernel subdirectory. Unfortunately, "Kernel" is not the default "Root" of a "Kernel" extension, so if you use a Kernel subdirectory you will need to include "Root"->"Kernel" in your extension specification, as shown. If you want your .wl file(s) to sit next to the PacletInfo.wl file, not in a Kernel subdirectory, then you would use a "Root" specification of "." in your Kernel extension.
It is not required that the context match the name of your paclet.

Subcontexts

If your main .wl file defines "MyContext`" and you have another .wl file , SomeSubcontext.wl, that defines the subcontext "MyContext`SomeSubcontext`", then where do you place that SomeSubcontext.wl file in your layout so that it will automatically be found? In other words, where should the SomeSubcontext.wl file reside so that the lookup of "MyContext`SomeSubcontext`" will resolve to that file? The answer is that it can reside right next to the MyContext.wl file. The paclet system supports resolving "MyContext`SomeSubcontext`" to a SomeSubcontext.wl file in your Kernel directory. You can think of "MyContext`" resolving to your Kernel directory by virtue of declaring "MyContext`" in your "Kernel" extension, and then the SomeSubcontext part resolves to the SomeSubcontext.wl file. If you have more deeply nested subcontexts like "MyContext`SomeSubcontext`AnotherSubcontext`", then the AnotherSubcontext.m file needs to reside in a Kernel/SomeSubcontext directory. The subcontext hierarchy needs to correspond to a directory hierarchy, except that the first part, "MyContext`" is "free", mapping to the Kernel directory itself.

init.m files

Many old-style applications use a special kernel feature for locating a file that corresponds to a context: If a context maps to a file location on $Path, but no appropriately named .wl or .m file is found there, the kernel looks for a Kernel subdirectory with an init.m file in it and reads that file. The init.m file typically just says something like Get[MyContext`MyContext`]. The double context will correctly map to a MyContext.m file in the MyContext parent directory. This is really just a trick to allow the context "MyContext`" to map to a MyContext.m file in a MyContext directory on $Path. This type of layout is supported in a paclet, but it is completely unnecessary and strongly discouraged. In the preceding example of a "Kernel" extension, there is no need to trick the system with a "double context". The context "MyContext`" maps directly to the Kernel/MyContext.m file, no init.m file needed.

Documentation

Paclets typically provide some documentation in Wolfram style, and if you have such documentation, then you declare a "Documentation" extension. The "Documentation" extension supports the standard "Root", "SystemID" and "WolframVersion" fields, and also a "Language" field. Most paclet authors will not need those fields, and instead rely on a conventional documentation layout:
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"Documentation"},
(* ... other extensions *)
}
|>]
With a PacletInfo.wl file like the preceding, this is the layout that your paclet would be expected to have:

2.gif

If you have documentation for languages other than English, you would have language-specific subdirectories alongside, or in place of, English.
The exact contents of the English directory are not specified in the preceding. The subject of authoring and building documentation is complex, and is described more fully in a later section.

FrontEnd

If your paclet has resources used by the notebook front end, like Stylesheets, Palettes, Bitmaps, TextResources, etc., it should declare a "FrontEnd" extension. Any stylesheets and palettes in your paclet will automatically show up in the appropriate places in the front end menus.
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"FrontEnd"},
(* ... other extensions *)
}
|>]
Although you can choose to specify a custom "Root" for this extension, most authors will use the default layout, which looks like:

3.gif

This is the same layout as seen in the front end itself: $InstallationDirectory/SystemFiles/FrontEnd.

Path

A paclet can declare that it has files that should be found by what look like path-based lookups, where the first part of the path is the paclet name.
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"Path"},
(* ... other extensions *)
}
|>]
Here is what your paclet layout might look like.

4.gif

For any function that expects a file path (like Get, OpenRead, FindFile, Import, etc.) you can now pass in "MyPaclet/SomeFile.txt". The first part of the path must match the paclet name. If the SomeFile.txt file was in a subdirectory named Stuff, you would specify "MyPaclet/Stuff/SomeFile.txt".

LibraryLink

If your paclet has LibraryLink libraries, you specify a "LibraryLink" extension:
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"LibraryLink"},
(* ... other extensions *)
}
|>]
Most paclet authors will rely on the default layout expected by LibraryLink. Here is what your paclet layout would look like.

5.gif

With this layout, LibraryLoad["MyLibrary"] would automatically work. There is no need to modify $LibraryPath, the value that LibraryLink uses for locating libraries not located in paclets.

JLink

If your paclet has Java classes to be used with J/Link, you specify a "JLink" extension:
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"JLink"},
(* ... other extensions *)
}
|>]
Most paclet authors will rely on the default root of the "JLink" extension, which is a Java directory. Here is what your paclet layout would look like.

6.gif

With this layout, J/Link will automatically be able to find classes in your jar files. There is no need to call J/Links AddToClassPath function.

Asset

All the extension types that have been described so far are things that automatically "weave" themselves into the system. You do not have to make a special paclet-aware call to, say, Needs in order to find a .wl file provided by a paclet. But there are many types of things that you might want to provide in a paclet for which there is no built-in handling in the Wolfram system. The "Asset" extension is a generic way to specify resources of arbitrary types that can be found by name using the "AssetLocation" selector.
Say that you have a executable in your paclet that you will launch with StartProcess. The best way to do that is to use an "Asset" extension to assign a name to that executable, and then look up its path via the name. In this way you avoid any hard-coded paths in your package code. It is only in the PacletInfo.wl file that the resource name is mapped to an actual file location, and that is the only place that needs to be changed to modify the location or even the filename itself. It also makes it easy to provide multiple versions of the executable, each for a different SystemID or WolframVersion.
Here is an example of a paclet that contains executables for three different SystemIDs:
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"Asset", "Root" "Mac", "SystemID" "MacOSX-x86-64", "Assets" {{"my_exe", "MyExe.app"}}},
{"Asset", "Root" "Windows", "SystemID" "Windows-x86-64", "Assets" {{"my_exe", "MyExe.exe"}}},
{"Asset", "Root" "Linux", "SystemID" "Linux-x86-64", "Assets" {{"my_exe", "MyExe"}}},
(* ... other extensions *)
}
|>]
The "Assets" field within the "Asset" extension is a list of pairs that map string identifiers to string paths. The paths can have directory components in them, like "subdir/MyExe.app".
Here is what your paclet layout might look like.

7.gif

In your package code, when you want to obtain the full path to the executable so you can launch it, you use the "AssetLocation" selector on the PacletObject:
The returned path will be different on different operating systems, and the code does not need to change if you rename or move the executable within the paclet layout.

Custom Extensions

You can also write custom extensions of your own creation. One reason for doing this is to implement a "plug-in" type of system. For example, say you are writing a framework for digital filters written in the Wolfram Language. Your paclet supplies code for working with these filters, and perhaps it supplies some filters of its own, but you want other paclets to be able to provide filters that can be automatically found at runtime. You can create a custom extension called, say, "DigitalFilters", and paclets that supply such filters will use it:
PacletObject[<|
"Name" "MyPaclet",
"Version" "0.0.1",
"Extensions" {
{"DigitalFilters", "Root" "Filters"},
(* ... other extensions *)
}
|>]
This paclet would then have a Filters directory with one or more .wl files in it:

8.gif

Your framework code can use the PacletManager`PacletResources function to locate all paclets that contain a "DigitalFilters" extension.
PacletManager`PacletResources returns a list of pairs: {{_PacletObject, {/full/path/to/extension/root}}, ...}. You can then extract the paths to all the extension roots and enumerate and load all the .wl files (or any other files) in those directories.
If you are wondering why PacletResources is in the "PacletManager`" context and not the "System`" context, it is simply because the design is still experimental. Functionality for this purpose will eventually become part of the main interface of the paclet system, but at this moment in time you can use PacletManager`PacletResources (and this function will be supported into the future).
Complex Example Paclet
Here is a hypothetical complex paclet that demonstrates many of the extension types and features discussed. If you can understand this paclet and how it matches up with the file layout, then you have a good understanding of paclet extensions.
PacletObject[<|
"Name" "MyPaclet",
"Version" "2.0.1.0.16382",
"SystemID" {"MacOSX-x86-64", "Windows-x86-64"}, (* this paclet will only be visible to Mac and Windows users *)
"WolframVersion" "12.1+", (* this paclet will only be visible to Wolfram Engine version 12 and later. *)
"Creator" "tgayley@wolfram.com",
"Description" "A demonstration paclet with no actual content.",
"Extensions" {
(* This extension provides versions of the .m files appropriate only for 12.1 *)
{"Kernel", "Root" "Kernel121", "WolframVersion" "12.1", "Context" {"MyPackage`", "MyOtherPackage`"}},
(* This extension provides versions of the .m files appropriate only for 12.2 and later *)
{"Kernel", "Root" "Kernel", "WolframVersion" "12.2+", "Context" {"MyPackage`", "MyOtherPackage`"}},
{"LibraryLink"},
{"Documentation"},
{"FrontEnd"},
{"Asset", "Root" -> "Assets", "Assets" {{"sun", "images/sun.jpg"}, {"moon", "images/moon.jpg"}}}
}
|>]
Here is the matching file layout:

9.gif

Installing and Updating
PacletInstall is the function that installs and updates paclets. Here the function installs a Wolfram paclet that is occasionally updated on the Wolfram paclet server:
Note that the result is a PacletObject expression, which is the Wolfram Language representation of a paclet. If you already had the Benchmarking paclet installed but there had been an update to it available on a server, that update would have been downloaded and installed by the command. If a paclet is already present on your machine, and no update is known to be available, then PacletInstall returns without taking any significant time, so if you are writing code that uses another paclet, you can call PacletInstall on that paclet and not be concerned about doing something unnecessarily expensive on your system or those of your users.
If you want to ensure that a paclet is installed, but not actually trigger an update if one is available, you can use the AllowVersionUpdate -> False option.
Paclets are installed into a special directory and are managed by the paclet system:
Although it is not the normal procedure, you can hand-copy paclet directories into the repository and delete them as well. The changes will not be detected until the next kernel restart, but you can call PacletDataRebuild[] to force the paclet system to immediately detect any changes you have made manually.

Installing from a .paclet file or URL

PacletInstall["PacletName"] is very convenient for installing and updating a paclet, but it only works for paclets that are deployed on a paclet server. Although it is very easy to establish your own paclet server (described later), you can also distribute paclets as .paclet files to be installed from the file system or from the web, including the Wolfram Cloud. A .paclet file is a packed version of a paclet directory, produced by the CreatePacletArchive function (described later). A .paclet file is what gets deployed to a paclet server, but you can also install it directly:
You can also manually copy a paclet directory into $UserBasePacletsDirectory/Repository. In this case, the paclet will only be detected the next time the kernel is restarted, but you can call PacletDataRebuild[] to force the paclet system to detect the change immediately.

PacletSites

PacletSites is the function that returns the list of paclet sites known to your system. At the time of this writing, for most users this will be just one site, the Wolfram public paclet server:
You can use PacletSiteRegister and PacletSiteUnregister to manage this list of known paclet sites. If you try to remove the main Wolfram site, it will be restored at the start of your next session. The only reason to use PacletSiteRegister and PacletSiteUnregister is if you want your system to know about additional paclet servers, for example, ones established by your organization. How to create such a paclet site is discussed later in this document.

PacletSiteUpdate

When you call PacletInstall to obtain or update a paclet, how does it know whether an update is available on a server? Users often assume that PacletInstall checks the server every time and is therefore an expensive operation, but this is not the case. PacletInstall only checks a local cache of data about what paclets are on the servers. That local cache of data is updated by calling PacletSiteUpdate:
This operation can take a second or more, depending on your geographical location and download speed.
PacletSiteUpdate is called internally at various times, at least once every few days, and generally much more often than that. If you want to be sure that you have the latest data about what paclets are available, you can call PacletSiteUpdate yourself. If you know that a paclet has been deployed to a server only very recently, you can call PacletSiteUpdate to make sure your system knows about the new version.
As mentioned previously, a call to PacletInstall generally does not trigger a call to PacletSiteUpdate, but one exception is if the paclet being requested is not installed locally and not found in the cache of server data. In this case, PacletSiteUpdate is called internally to see if the paclet is available, rather than just failing based on potentially outdated cache information.
Finding Paclets
Paclets are designed to weave themselves into the system so that there is little need to operate on them directly. The Mathematica 13 layout, for example, has hundreds of paclets in it, and they are silent components of the system; you do not need to know any paclet system functionality to use them. Sometimes, however, it is useful to inspect paclets on the system and perform operations with them, and this is often necessary for developers who are creating their own paclets.

PacletFind

PacletFind is the function that finds paclets that have been installed on the system. It returns a list of PacletObject expressions. PacletObject is the representation of a paclet in the Wolfram Language and can be used to inquire about various paclet properties. PacletFind takes an Association of properties that control what paclets it returns.
There are three different versions of the NeuralNetworks paclet on the following machine:
The paclet system only uses the one with the highest version number (and this list is in sorted order), but PacletFind returns all the paclets that are compatible with the current system. If you are wondering why there are three different versions, it is because one is in the Mathematica 13 layout, and the other two are in the local repository, having been downloaded as updates in older versions of Mathematica. These paclets show up in the output because they are still compatible with Mathematica 13, but only Version 13.0.3 will be used, because it has the highest version number.
Here you see the locations of the paclets, showing that the two older ones are in the local repository of installed paclets.
By default, PacletFind only returns paclets that are compatible with the current SystemID and version of the Wolfram Engine, but you can use the "SystemID"->All and "WolframVersion"->All options to show even ones that are not compatible.
You can use the * wildcard to see paclets with names that match a pattern:
This asks for all paclets whose names start with N and that have a "Documentation" extension:

PacletFindRemote

PacletFindRemote is the function that finds paclets available on a paclet server.
This shows that there are several versions of NeuralNetworks available that are compatible, but none of these would be be downloaded if PacletInstall["NeuralNetworks"] was used, because they all have versions lower than the one already installed. PacletFindRemote is not a function that users have much reason to call, but paclet developers can use it to understand what is available on servers and determine if an update is available to a given paclet.
Getting Information about Paclets
There are several ways to get information about specific paclets. In most cases, the first step is to obtain the PacletObject expression that represents the paclet you want to know about. One way to get a PacletObject expression is to use the PacletFind function:
PacletFind returns a list in sorted order, so the paclet that is in use by the system, which is the one with the highest version number, is listed first. If you just want the active version of a paclet, you can use PacletObject["pacletname"]:
PacletObject["pacletname"] is essentially equivalent to First[PacletFind["pacletname"]].
The Information function is an easy way to get a reasonably full set of data:
You can also call it with a selector:
Most extraction of information about a paclet is done with property extraction syntax applied to the PacletObject itself:
You can extract multiple properties at once:
Developing Paclets
Paclets are an ideal packaging format for almost any kind of Wolfram System functionality that is not simple enough to be a candidate for one of the specialized repositories (Function Repository, Data Repository, etc.)
The PacletTools paclet is provided to assist users in creating paclets, but you can also do it easily by hand. The following works through a simple example of a paclet that contains one .wl file.
Start off by creating a directory for your paclet, say $UserDocumentsDirectory/MyPaclet. In this directory, create a PacletInfo.wl file with the following content. You can use the notebook front end or any text editor to create this:
PacletObject[<|
"Name" "MyPaclet",
"Version" "1.0",
"Extensions" {
{"Kernel", "Root" "Kernel", "Context" {"MyContext`"}}
}
|>]
Note that the actual name of the directory (MyPaclet) is not relevant. It could have any name, not necessarily matching the name in the PacletInfo.wl file. The paclet name comes from PacletInfo.wl, and it is only a common convention for the parent directory to have the same name.
Create a Kernel subdirectory of MyPaclet. This is the directory that will hold the MyContext.wl file that contains the function definitions that you want to provide. It is also not necessary for the name of the context ("MyContext`") to match the paclet name, but it usually will.
Create a MyContext.wl file in the MyPaclet/Kernel directory and give it the following trivial package content:
BeginPackage["MyContext`"]

Squared

Begin["`Private`"]

Squared[x_] := x^2

End[]

EndPackage[]
You have now created a paclet. To distribute this to others, you want to pack it up as a .paclet file (which is really just a zip file, but it has some special conventions, so always use CreatePacletArchive instead of another archiving utility). CreatePacletArchive produces a .paclet file next to the directory that it is given to pack:
Note that CreatePacletArchive creates a filename that mashes together the paclet name and version number. This precise filename is not required for the paclet, and it could be renamed to anything (still keeping the .paclet extension). It is what is in the PacletInfo.wl file that matters to the paclet.
You can now distribute the MyPaclet-1.0.paclet file to colleagues or deploy it to a paclet server.
To see it running in the current session, install it. This unpacks the .paclet file into $UserBasePacletsDirecory/Repository and makes it available to the system:
Load the paclets context and use it in the usual way:
For what follows, it is necessary to uninstall the paclet. This deletes the paclet directory from the install location.

PacletDirectoryLoad and PacletDirectoryUnload

The preceding section was a walkthrough of the development and deployment process of a trivial paclet. If you are actively developing the code in a paclet, you probably do not want to go through the process of packing it into a .paclet file and installing it just so that you can test your latest changes. This is where the PacletDirectoryLoad function comes in. PacletDirectoryLoad tells the paclet system to look for paclets in a nonstandard location. The directory you add will be searched for paclets, up to two levels deep, and these paclets will be made available to the system immediately. The added directory only applies for the current kernel session. You can think of PacletDirectoryLoad as the analog of PrependTo[$Path, "dir"] in the non-paclet world.
A typical workflow while developing a paclet is to create your files in the standard paclet layout and then edit them in place. You can call PacletDirectoryLoad["/my/development/dir"] to let the paclet system load your development version of the paclet. You only need to do this once in a session. Now you can edit your .wl files and call Get["MyContext`"] repeatedly to reload them, picking up your newest changes.
If you modify your PacletInfo.wl file during development, say by adding a new extension type, you will need to call PacletDataRebuild[] to let the paclet system know it needs to rebuild its cache of paclet data.
A convenient feature of PacletDirectoryLoad is that if a paclet in the given directory has the exact same version number as one installed elsewhere, then the paclet from PacletDirectoryLoad will be used in preference to the other paclet. The rule can be stated as "PacletDirectoryLoad wins ties". This means that if you are developing a paclet, and you have already installed Version 1.0.0 of that paclet in your local repository, you can use PacletDirectoryLoad to point at your development version of the paclet without having to increment the version number in your development PacletInfo.wl file. It is absolutely critical that whenever you release a modification of any kind to a paclet, you must give it a higher version number than any previous version. But when you are doing development, it is convenient not to have to continually increment the version number.
PacletDirectoryUnload removes a directory from the paclet search path, immediately detaching any paclets in those directories from the system. For example, Needs will no longer find contexts in those paclets, and any palettes or other front end resources will disappear from front end menus. Of course, if any code has already been loaded from a paclet, then PacletDirectoryUnload will not wipe that out of your session.

PacletDataRebuild

PacletDataRebuild is the function you need to call whenever you alter paclets outside of normal paclet system functions (like PacletInstall). If you are using built-in paclet system functions only, then the paclet system keeps track of any changes, but if you make a change by hand, say by altering a PacletInfo.wl file, then the paclet system has no way of knowing that something has changed and that its cache might be out of date. Therefore, you need to run PacletDataRebuild[], which causes the paclet system to rescan all the PacletInfo.wl files in the system. This only takes a second or two.
Users often call PacletDataRebuild in cases where it is not necessary, and although it is safe to do this, you should understand what it does and that it is only necessary on occasions where you make manual changes to PacletInfo.wl files or hand-copy paclet directories into or out of $UserBasePacletsDirectory/Repository.

Authoring and Building Documentation

Creating and building documentation is a complex part of paclet development. The documentation that ships with Wolfram products is created in a certain format, with a set of specialized tools, and then it is processed by another specialized tool into the files that you see in the product. Paclet authors want to be able to create documentation that looks and behaves like built-in documentation, and the PacletTools and DocumentationTools paclets are provided to assist in this task. See the documentation for those paclets for details.
Paclet Servers
It is easy to set up a paclet server on your own machine or for your company or organization. No logic is required on the server, and in fact a paclet "server" can be as simple as a directory visible on the network with a few special files in it. The paclet system has tools to automatically create the required files.
Say that you have a set of .paclet files that you have built or been given by others, and you want to make them findable and installable by others in your organization. All you need is a directory that can be accessed by others on your network via a file: URL. Here is an example of setting up a paclet "server" in the PacletServer directory of your home directory. In this PacletSite directory, you must first create a Paclets subdirectory (it must have this name), then place the desired collection of .paclet files in it. Now call the PacletManager`BuildPacletSiteFiles function, pointing at the PacletServer directory:
This function examines all the .paclet files, extracts information from them, and creates two files in the PacletSite directory: PacletSite.m and PacletSite.mz. These are the index files that clients will read when you call PacletSiteUpdate. Once those two files have been created, you now have a functioning paclet site. You add it via a file: URL.
Commands like PacletInstall and PacletFindRemote will now include this paclet server when looking for the latest versions of paclets.
The procedure for setting up a webserver-based paclet site is very similar. You create the same directory structure, a parent directory with a Paclets subdirectory containing .paclet files, then call PacletManager`BuildPacletSiteFiles on that parent directory. You simply make this directory visible to your webserver, and give people the URL to the parent directory, perhaps something like:
When new paclets are to be added to the server, just run BuildPacletSiteFiles again. If paclets are updates to older versions, delete the older versions from the Paclets directory before calling BuildPacletSiteFiles.