Custom Metadata
EnhancedAdvanced
Overview
Posit Package Manager allows administrators to define custom key-value pairs as metadata for packages with flexible matching criteria.
Package metadata can be added globally, affecting multiple packages across a source or repository; or they can be as specific as a single package version in one source or repository.
Custom package metadata support is available at the Enhanced tier for local use and Advanced tier for remote use.
Custom Metadata System
Package metadata utilizes a criteria matching system to apply key-value pairs to packages with increasing specificity the more criteria are matched. As examples of how custom metadata can be applied, you can:
- Add a risk score to all packages in a repository
- Add a risk score to a single version of a specific package
- Add a documentation link to a specific package
The matching criteria for package metadata is displayed when creating metadata. Use the rspm create metadata or rspm create bulk-metadata commands to create new metadata, and rspm list metadata command to list them.
Terminal
# Add a high risk score to all CRAN packages
rspm create metadata --source=cran --key=risk --value=10
# Add a high risk score to django 1.0.0
rspm create metadata --package-name=django --version='1.0.0' --key=risk --value=10
# Add a low risk score to django 1.0.0 in an exempt source
rspm create metadata --package-name=django --version='1.0.0' --source="exempt-source" --key=risk --value=0
# List all metadata
rspm list metadata
ID: 1 (global)
-- Data:
- Key: risk
- Value: 10
-- Constraints:
- Source: cran
ID: 2 (Package: django)
-- Data:
- Key: risk
- Value: 10
-- Constraints:
- Version: 1.0.0
ID: 3 (Package: django)
-- Data:
- Key: risk
- Value: 0
-- Constraints:
- Source: exempt-source
- Version: 1.0.0Custom Tabs
Use custom metadata to add custom tabs to a package’s details page. Use keys prefixed with tab. to create tabs:
Terminal
# Add a "Docs" tab linking to external documentation for a specific package
rspm create metadata --package-name=ggplot2 --key=tab.docs --value=https://ggplot2.tidyverse.org
# Add a "Setup" tab for all packages in a repository (using an embedded PPM page)
rspm create metadata --repo=cran --key=tab.setup --value=https://ppm.example.com/client/#/repos/cran/setup
# Add a "User Guide" tab for all packages from a specific source
rspm create metadata --source=pypi --key=tab.user-guide --value=https://docs.example.com/python-packages
# Add a "Configuration" tab for a VS Code extension
rspm create metadata --package-name=posit.shiny --key=tab.configuration --value=https://shiny.posit.co/py/docs/install-create-run.html#vs-code
# Add a "Docs" tab to an agent plugin's detail page
rspm create metadata --package-name=my-claude-plugin --key=tab.docs --value=https://example.com/docsTo update an existing tab’s URL, use the --update-existing flag:
Terminal
rspm create metadata --package-name=ggplot2 --key=tab.docs --value=https://ggplot2-book.org --update-existingTo remove a custom tab, use rspm delete metadata:
Terminal
# List metadata to find the ID of the tab you want to remove
rspm list metadata --package-name=ggplot2
# Delete by ID
rspm delete metadata --id=42Plugins
AI agent plugins (see AI Agent Plugins) support the same custom metadata system. Use --repo to scope metadata to a plugins repository:
Terminal
# Tag an agent plugin with its owning team
rspm create metadata --package-name=my-claude-plugin --repo=my-plugins --key=team-owner --value=backend
# Mark a plugin as approved across all repos
rspm create metadata --package-name=my-claude-plugin --key=approved --value=trueBulk Creation
Bulk creation of metadata can be done both via JSON and CSV input files. See rspm create bulk-metadata for more information.
Terminal
# View the shape of the JSON
cat /tmp/metadata.json
[
{
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"repo": "cran",
"source": "cran"
},
{
"package_name": "ggplot2",
"key": "risk",
"value": "5",
"repo": "cran",
"source": "cran",
"version": "1.0.0"
}
]
# Bulk create the metadata
bin/rspm create bulk-metadata --path=/tmp/metadata.json
{
"affected": [
{
"id": 1,
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
}
]
},
{
"id": 2,
"package_name": "ggplot2",
"key": "risk",
"value": "5",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
},
{
"version": "1.0.0"
}
]
}
],
"rejected": null
}Terminal
# View the shape of the CSV
cat /tmp/metadata.csv
"value","key","source","repo","version","package_name"
"10","risk","cran","cran","","ggplot2"
"5","risk","cran","cran","1.0.0","ggplot2"
# Bulk create the metadata
rspm create bulk-metadata --csv-in=/tmp/metadata.csv
{
"affected": [
{
"id": 1,
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
}
]
},
{
"id": 2,
"package_name": "ggplot2",
"key": "risk",
"value": "5",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
},
{
"version": "1.0.0"
}
]
}
],
"rejected": null
}Matching Criteria
Custom metadata supports several matching criteria. These include:
- Package name: Name of the package. R packages are case-sensitive, while Python packages are case-insensitive.
- Version: A package version.
- Source: Name of the source, such as
cranorpypi. All source types are supported. - Repository: Name of the repository.
For more information on the matching criteria, reference the CLI appendix.
Specificity and Conflicting Key-Value Pairs
When multiple matching keys are matched to a package, the key-value pair with the highest specificity takes precedence.
Examples
Terminal
ID: 1 (global)
-- Data:
- Key: risk
- Value: 60
-- Constraints:
- Source: cran
ID: 2 (Package: plumber)
-- Data:
- Key: risk
- Value: 70
ID: 3 (Package: plumber)
-- Data:
- Key: risk
- Value: 80
-- Constraints:
- Version: 1.0.1
ID: 4 (Package: plumber)
-- Data:
- Key: risk
- Value: 90
-- Constraints:
- Source: cran
- Version: 1.0.1
ID: 5 (Package: plumber)
-- Data:
- Key: risk
- Value: 95
-- Constraints:
- Source: cran
- Repo: cran
- Version: 1.0.1Given the above set of 5 metadata records, the plumber package with version 1.0.1 in the cran repository will have a risk score of 95.
Editing Metadata
Custom metadata can be edited using the same rspm create metadata command, by passing the --update-existing flag.
Following the example above for creating new metadata, we can update it like this:
Terminal
# Lower the risk score of all CRAN packages
rspm create metadata --source=cran --key=risk --value=5 --update-existing
# List all metadata
rspm list metadata
ID: 1 (global)
-- Data:
- Key: risk
- Value: 5
-- Constraints:
- Source: cranBulk Edit
Bulk edits of metadata is done via the rspm create bulk-metadata command. There are no additional flags necessary, the command automatically updates existing records if the value has changed.
Terminal
# View existing metadata
rspm list metadata --output-format=json
[
{
"id": 1,
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
}
]
},
{
"id": 2,
"package_name": "ggplot2",
"key": "risk",
"value": "5",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
},
{
"version": "1.0.0"
}
]
}
]
# View the shape of the JSON
cat /tmp/metadata.json
[
{
"package_name": "ggplot2",
"key": "risk",
"value": "20",
"repo": "cran",
"source": "cran"
},
{
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"repo": "cran",
"source": "cran",
"version": "1.0.0"
}
]
# Bulk edit the metadata
rspm create bulk-metadata --path=/tmp/metadata.json
{
"affected": [
{
"id": 1,
"package_name": "ggplot2",
"key": "risk",
"value": "20",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
}
]
},
{
"id": 2,
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
},
{
"version": "1.0.0"
}
]
}
],
"rejected": null
}Terminal
# View existing metadata
rspm list metadata --output-format=json
[
{
"id": 1,
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
}
]
},
{
"id": 2,
"package_name": "ggplot2",
"key": "risk",
"value": "5",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
},
{
"version": "1.0.0"
}
]
}
]
# View the shape of the CSV
cat /tmp/metadata.csv
"value","key","source","repo","version","package_name"
"20","risk","cran","cran","","ggplot2"
"10","risk","cran","cran","1.0.0","ggplot2"
# Bulk edit the metadata
rspm create bulk-metadata --csv-in=/tmp/metadata.csv
{
"affected": [
{
"id": 1,
"package_name": "ggplot2",
"key": "risk",
"value": "20",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
}
]
},
{
"id": 2,
"package_name": "ggplot2",
"key": "risk",
"value": "10",
"constraints": [
{
"repo_id": 2,
"repo": "cran",
},
{
"source_id": 1
"source_name": "cran"
},
{
"version": "1.0.0"
}
]
}
],
"rejected": null
}Remote Usage
The metadata commands can also be used remotely, e.g. rspm create metadata. This allows automating metadata management.
When generating a token to set metadata remotely, the rspm create token command must include either --scope=metadata:admin or --scope=global:admin. The metadata:admin scope gives full access to create, update, delete, and list metadata.
For more information on remotely adding or editing metadata, refer to the Admin CLI - Remote Use section.
Remote usage of custom metadata commands is available at the Advanced tier.