{"id":50566732,"url":"https://github.com/v--/viat","last_synced_at":"2026-06-04T15:30:42.356Z","repository":{"id":357237721,"uuid":"1236014654","full_name":"v--/viat","owner":"v--","description":"A tool for managing virtual file attributes","archived":false,"fork":false,"pushed_at":"2026-05-12T00:09:06.000Z","size":126,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-12T00:27:57.339Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/v--.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T21:47:05.000Z","updated_at":"2026-05-12T00:08:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/v--/viat","commit_stats":null,"previous_names":["v--/viat"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/v--/viat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/v--%2Fviat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/v--%2Fviat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/v--%2Fviat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/v--%2Fviat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/v--","download_url":"https://codeload.github.com/v--/viat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/v--%2Fviat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33912317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-04T15:30:41.194Z","updated_at":"2026-06-04T15:30:42.350Z","avatar_url":"https://github.com/v--.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Viat\n\n[![Tests](https://github.com/v--/viat/actions/workflows/test.yml/badge.svg)](https://github.com/kcroker/dpsprep/actions/workflows/test.yml)\n[![ReadTheDocs](https://img.shields.io/readthedocs/viat)](https://viat.readthedocs.io/)\n[![PyPI Package](https://img.shields.io/pypi/v/viat)](https://pypi.org/project/viat/)\n[![AUR Package](https://img.shields.io/aur/version/viat)](https://aur.archlinux.org/packages/viat)\n\nA tool for managing **vi**rtual file **at**tributes.\n\nViat allows recording file attributes in a plain text file. The main unit of operation is a vault, which is determined by `.viat` subdirectory. In the simplest case, this subdirectory contains `config.toml`, `storage.toml` and possibly `schema.json`.\n\nIn short, in an empty vault, the command\n\n```shell\nviat set file.pdf --raw attr value\n```\n\nputs the following into `storage.toml`:\n\n```toml\n[\"file.pdf\"]\nattr = \"value\"\n```\n\nDetermining which files are tracked by Viat is done via [tracker providers](https://viat.readthedocs.io/en/stable/api/providers/tracker/), while storing the attributes is done via [storage providers](https://viat.readthedocs.io/en/stable/api/providers/storage/). Both protocols are very general and new providers can easily be added.\n\n## Table of contents\n\n* [Usage](#usage)\n* [Installation](#installation)\n* [Motivation](#motivation)\n\nSee also the [documentation on ReadTheDocs](https://viat.readthedocs.io/)\n\n## Usage\n\nWe give a usage tutorial here; refer to the [online documentation](https://viat.readthedocs.io/) or to the man page [`viat(1)`](https://viat.readthedocs.io/en/stable/man/) for more details.\n\n### Command-line usage\n\nFirst, a vault must be initialized:\n\n```shell\nviat init\n```\n\nThe vault is determined by a `.viat` subfolder that contains `config.toml` and `storage.toml` files (JSON is also supported for both). We can immediately set attributes for any file on the file system:\n\n```shell\n$ viat update tractatus.pdf '{\"author\": \"Ludwig Wittgenstein\", \"year\": 1921}'\nWarning: File 'tractatus.pdf' is not being tracked.\n{\"author\": \"Ludwig Wittgenstein\", \"year\": 1921}\n```\n\nAll stored attributes for the file get printed; in this case the only stored attributes are those we have just added. We also get a warning saying that the vault's tracker does not know about this file.\n\nThe role of the tracker is to enumerate the files that are explicitly tracked by the vault. The default glob-based tracking provider requires explicit patterns. We can track all PDF files in the root of the vault using the following configuration:\n\n```toml\n[tracker.glob]\npatterns = [\"*.pdf\"]\n```\n\nWith this, we can add new properties without warnings:\n\n```shell\n$ viat set tractatus.pdf rating 4\n{\"author\": \"Ludwig Wittgenstein\", \"year\": 1921, \"rating\": 4}\n```\n\nThe above worked because \"true\" is a valid JSON value; if we were to set a string instead, we would have to escape it in quotes, which is inconvenient. Instead, we can treat the value as a string by passing the `--raw` flag:\n\n```shell\n$ viat set --raw tractatus.pdf publisher 'Annalen der Naturphilosophie'\n...\n```\n\n#### Scripting\n\nTracking is useful for ensuring consistency with the file system, but also for shell scripting. For example, the following command produces a table of variables:\n\n```shell\n$ viat shell-export\npath=tractatus.pdf publisher='Annalen der Naturphilosophie' rating=4 author='Ludwig Wittgenstein' year=1921\n```\n\nThis can be utilized in bash as follows:\n\n```bash\nviat shell-export | while read line; do\n    eval \"export $line\"\n    # The attributes are now exported as variables\ndone\n```\n\nIn fish shell this is even simpler:\n\n```fish\nfor line in (viat shell-export)\n    eval \"export $line\"\n    # The attributes are now exported as variables\nend\n```\n\nIf we add another file, `zarathustra.pdf`, and if the tracker lists it after `tractatus.pdf`, then Viat would try to reset the missing attributes to avoid reusing variables from the previous loop iteration:\n\n```shell\n$ viat shell-export\npath=tractatus.pdf publisher='Annalen der Naturphilosophie' rating=4 author='Ludwig Wittgenstein' year=1921\npath=zarathustra.pdf publisher= rating= author= year=\n```\n\n#### Schemas\n\nIt makes sense to utilize JSON schemas. Let us add the following to `.viat/schema.json`:\n\n```json\n{\n  \"type\":\"object\",\n  \"properties\": {\n    \"year\": {\"type\": \"number\"}\n  }\n}\n```\n\nNow we can no longer set the year to anything that is not a number:\n\n```shell\n$ viat set tractatus.pdf --raw year string\nError: Validation error for 'tractatus.pdf': data.year must be number.\n```\n\nThe essence of the tool is that the attributes are stored in plain text formats that can be edited committed to version control. For example, `.viat/storage.toml` should now look as follows:\n\n```toml\n[\"tractatus.pdf\"]\nauthor = \"Ludwig Wittgenstein\"\nyear = 1921\nrating = 4\npublisher = \"Annalen der Naturphilosophie\"\n```\n\nIf we manually change the year to \"string\", we will get a warning when loading the vault:\n\n```shell\n$ viat get tractatus.pdf rating\nWarning: Validation error in stored data for 'tractatus.pdf': data.year must be number.\n4\n```\n\n#### (Re)moving files\n\nIf we move `tractatus.pdf` to `book.pdf`, viat will no longer know about it:\n\n```shell\n$ viat get book.pdf rating\nWarning: File 'book.pdf' is not being tracked.\nError: Attribute 'rating' has not been set for 'book.pdf'.\n```\n\nSuch discrepancies can be determined relatively easily:\n\n```bash\n$ viat stale\ntractatus.pdf\n$ viat tracked --no-data\nbook.pdf\n```\n\nFor such cases, we provide the helpers `viat mv` and `viat rm`, but otherwise avoid being too clever.\n\n### Programmatic usage\n\nThe programmatic usage is straightforward enough because of the [API reference](https://viat.readthedocs.io/en/stable/api/protocols/). Here is a brief continuation of the above example:\n\n```python\nvault = autoload_vault()\n\nassert vault.tracker.is_tracked('tractatus.pdf')\n\n# This context manager validates and writes the file upon exiting.\n# If no mutators have been used, no validation and writing is performed.\nwith vault.storage as conn:\n    # The inner lock-based context managers allow either read-only or read-write operations.\n\n    # Readers are Mapping instances.\n    with conn.get_reader('tractatus.pdf') as reader:\n        print(mut['year'])\n\n    # Mutators are MutableMapping instances.\n    with conn.get_mutator('tractatus.pdf') as mut:\n        mut['year'] = 1921\n```\n\n## Installation\n\nThe [`viat` PyPI package](https://pypi.org/project/viat/) contains the core programmatic API.\n\nTo install the `viat` executable for the current user, you can use [`pipx`](https://pipx.pypa.io) or [`uv`](https://docs.astral.sh/uv/):\n\n```shell\npipx install viat\nuv tool install viat\n```\n\nThe git tracker requires the `git` extra.\n\nTo install from GitHub, you must use the following:\n\n```shell\nuv tool install viat --from git+https://github.com/v--/viat\n```\n\nSometimes a particular feature branch need to be tested. For installing a fixed revision (i.e. common/branch/tag), the following should work (if `extra-name` is needed, use `viat@rev[extra-name]`):\n\n```shell\nuv tool install viat --from git+https://github.com/v--/viat@rev\n```\n\nTo install `viat` from a cloned repository, you can use the following:\n\n```shell\nuv sync\nuv build --wheel\n# Once built, we can install using uv\nuv tool install viat --from dist/*.whl\n# or pipx\npipx install --include-deps dist/*.whl\n```\n\nTasks inside the repository like linting and testing use are summarized in [`poe.toml`](./poe.toml) (configuration for [poethepoet](https://pypi.org/project/poethepoet/)). For example, building the documentation requires some hacks, but can be done using a single command:\n\n```shell\nuv run poe docs-build\n```\n\n\u003e [!TIP]\n\u003e An [AUR package](https://aur.archlinux.org/packages/viat) is available for reference, as well as a [GitHub Action](.github/workflows/test.yml). If you are packaging this for some other package manager, consider using PEP-517 tools as shown in [this PKGBUILD file](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=viat).\n\n## Motivation\n\nWhen managing lots of files, there comes a point when metadata needs to be attached to them somehow.\n\n* Different file systems offer [extended file attributes](https://en.wikipedia.org/wiki/Extended_file_attributes). Unfortunately, poor software support reduces their utility. For example, `curl --xattr \u003curl\u003e` will record some attributes, but they will be lost on copy (with GNU `cp` at least) and will not be tracked by git.\n\n* [git attributes](https://git-scm.com/docs/gitattributes) are obviously supported by git, but other tools have to consult git in order to use them. Furthermore, there is no convenient mechanism for setting git attributes.\n\n* XMP ([extensible metadata platform](https://developer.adobe.com/xmp/docs/)) files are designed to be used by arbitrary tools and can be easily tracked using version control, but are cumbersome to manage.\n\nPerhaps I am missing some other approaches, but at this point it should be clear that there is no convenient way to manage file metadata. A long time ago I wrote a small script that tracked \"virtual\" attributes across a directory by putting them into a single JSON file. At some point I decided to refine the script, and so Viat was born.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fv--%2Fviat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fv--%2Fviat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fv--%2Fviat/lists"}