Async Python 3 library wrapping the TAK Server HTTP API. All network I/O uses
asyncio + aiohttp with certificate-based mutual TLS authentication.
Note
By default the client does not verify the server certificate
(CERT_NONE) because TAK Servers typically run self-signed certs -
only the client certificate is presented for mutual TLS. Pass
ca_cert="/path/to/ca.pem" to Server() to verify the server
against a CA (or pin its self-signed certificate directly); hostname
checking is then enabled too.
Documentation lives in the project wiki: https://github.com/sgofferj/python-takserver-api.wiki
The library is organized into a Server class with sub-api accessors,
each wrapping operations of one or more tags of the live OpenAPI spec
(tests/openapispec.json). Coverage numbers below are generated from the
spec vs. the actual wrappers (see scripts/generate_coverage_badge.py):
| Class | Accessor | Coverage | Status |
|---|---|---|---|
HomeApi |
server.home |
3 of 4 spec operations | Complete; getVer broken server-side, not wrapped |
MissionApi |
server.mission |
103 of 109 spec operations | Complete except mission-parent and guid layer-position reordering |
UserAccountManagementApi |
server.user |
10 of 10 spec operations | Complete, incl. helpers |
GroupApi |
server.groups |
4 of 6 groups-api operations (+2 LDAP) | Complete for group subscriptions; user-listing ops not wrapped |
SubscriptionApi |
server.subscriptions |
12 of 12 subscription-api operations | Complete, incl. helpers |
DataFeedApi |
server.datafeeds |
11 of 11 spec operations | Complete, incl. helpers |
CertManagerApi |
server.certs |
10 of 15 spec operations | Complete; 5 ops broken server-side, not wrapped |
SubmissionApi |
server.submission |
15 of 15 spec operations | Complete; creation ops broken server-side, guarded |
Home API — server.home
is_admin()·get_home()·get_user_roles()- Helpers:
has_role()·server_version()
User Account Management API — server.user
- Users:
get_all_users()·user_exists()·create_or_update_file_user()·change_user_password()·delete_user()·create_file_users_in_bulk() - Groups:
get_all_group_names()·group_exists()·get_users_in_group()·update_users_for_group()·get_groups_for_user()·update_groups_for_user()
Group API — server.groups (channel subscriptions)
- Catalog & lookup:
get_all_groups()·get_group()·get_channels()·channel_exists() - Subscriptions:
set_active_groups()·set_active_groups_bits()·set_active_groups_force()·wait_for_group_update() - Helpers:
get_active_groups()·subscribe()/subscribe_many()·unsubscribe()/unsubscribe_many()·is_subscribed()·wait_for_group_update_until() - LDAP:
get_ldap_groups()·get_ldap_group_members() - Note: subscription helpers need the caller's username — pass
Server(..., username=...)(the certificate CN) or give an explicitusername=argument. See the Groups-API wiki page for scope notes (entitlements vs. available channels vs. active subscriptions).
Data Feed API — server.datafeeds
- Catalog:
get_data_feeds()·get_data_feeds_in_bbox()·get_data_feeds_in_polygon() - Predicate feeds:
build_predicate_feed()·create_predicate_data_feed()·update_predicate_data_feed()·delete_predicate_data_feed()·get_predicate_data_feed() - Stats & content:
get_stats()·get_stats_for_feed()·get_existing_cot_types()·get_cots_by_cot_type() - See the Data-Feeds-API wiki page for the filter-groups access-lockout trap.
Cert Manager API — server.certs (admin only)
- Listing:
get_certificates(username=None)·get_active_certificates()·get_expired_certificates()·get_replaced_certificates()·get_revoked_certificates() - Records:
get_certificate()·download_certificate() - Mutations:
revoke_certificates()·delete_certificates()·delete_certificate() - The user-side TLS enrollment endpoints are not wrapped (server answers 403 even to admins); see the Cert-Manager-API wiki page.
Submission API — server.submission (input & messaging config)
- Inputs:
get_input_metrics()·get_input_metric()·create_input()·modify_input()·delete_input() - Streaming feeds by name:
create_data_feed()·get_data_feed()·modify_data_feed()·delete_data_feed() - Config & features:
get_config_info()·modify_config_info()·is_store_forward_chat_enabled()·enable/disable_store_forward_chat() - Counters:
get_database_cot_counts() - Creating inputs/named feeds is broken server-side (HTTP 400, NPE in validation); see the Submission-API wiki page.
Mission API — server.mission
- All name-based mission endpoints of the live spec: lifecycle (create/delete/copy/archive/expiration/parent), content, content keywords, external data, invitations, passwords, tokens, layers, map layers, feeds, logs and subscriptions
- The spec's parallel
/missions/guid/{guid}endpoints are covered through query-parameter equivalents on the name-based paths - Global endpoints: mission count/names, paged list, all-invitations/logs/subscriptions
- Not wrapped: mission-parent reassignment (
DELETE/PUT .../missions/{name}/parent), guid-based layer-position reorder andPOST /Marti/api/missions/{name}(creation goes through the body/query variant) - Extras:
build_mission_package()/add_mission_package()and the single-keyword content helpersdelete_content_keyword_by_hash()/delete_content_keyword_by_uid()
Endpoints that are proven not to work on the reference server are
deliberately NOT wrapped (e.g. home-api getVer / GET /Marti/api/ver
returns HTTP 500); see the wiki "Not implemented" sections.
The user API wrappers always send all three group-list fields (groupList,
groupListIN, groupListOUT) - the reference server answers HTTP 500
when any is omitted. Groups are implicit: they appear when a user carries
them and disappear when no user has them. See the wiki for details.
The current state of this library is tested against a real TAK server running
5.7-RELEASE-43-HEAD (reportable at runtime via
await server.home.server_version(), verified 2026-08-10).
The API specification used as the development baseline is
tests/openapispec.json, pulled live from the test server's
/v3/api-docs endpoint.
The live tests in live_tests/ are never part of CI; they run only against
the developer's own TAK server. Configure that server in an untracked .env
file in the repository root (.env is gitignored):
TAK_LIVE_HOST=tak.example.com TAK_LIVE_CERT=path/to/client.pem TAK_LIVE_KEY=path/to/client.key
Without a complete .env the live tests skip automatically. Never commit
the server address or any credentials.
For more controlled deployments and to get rid of "works on my computer" -syndrome, we always make sure our software works under docker.
It's also a quick way to get started with a standard development environment.
We need buildkit:
export DOCKER_BUILDKIT=1
And also the exact way for forwarding agent to running instance is different on OSX:
export DOCKER_SSHAGENT="-v /run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock -e SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock"
and Linux:
export DOCKER_SSHAGENT="-v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK -e SSH_AUTH_SOCK"
Build image, create container and start it:
docker build --ssh default --target devel_shell -t python_takserver_api:devel_shell . docker create --name python_takserver_api_devel -v "$(pwd):/app" -it $(echo $DOCKER_SSHAGENT) python_takserver_api:devel_shell docker start -i python_takserver_api_devel
If working in Docker instead of native env you need to run the pre-commit checks in docker too:
docker exec -i python_takserver_api_devel /bin/bash -c "pre-commit install --install-hooks" docker exec -i python_takserver_api_devel /bin/bash -c "pre-commit run --all-files"
You need to have the container running, see above. Or alternatively use the docker run syntax but using the running container is faster:
docker run --rm -it -v "$(pwd):/app" python_takserver_api:devel_shell -c "pre-commit run --all-files"
You can use the devel shell to run py.test when doing development, for CI use the "tox" target in the Dockerfile:
docker build --ssh default --target tox -t python_takserver_api:tox . docker run --rm -it -v "$(pwd):/app" $(echo $DOCKER_SSHAGENT) python_takserver_api:tox
There's a "production" target as well for running the application, remember to change that architecture tag to arm64 if building on ARM:
docker build --ssh default --target production -t python_takserver_api:latest . docker run -it --name python_takserver_api python_takserver_api:amd64-latest
Alpine images are much more lightweight than Debian/Ubuntu ones so they are preferred where possible. There are a few potential issues however:
- Compiled extensions not available as wheels. This is mostly mitigated by our own wheel builder.
- Compiled extensions not compiling under Alpine. Alpine does not have certain nonstandard extensions to libc enabled by default, poorly written extensions will fail to compile because they depend on these extensions and do not explicitly request them to be enabled.
- Poetry lockfile might need to be updated by running poetry inside Alpine Docker (use devel_shell above)
TLDR:
Create and activate a Python 3.11+ virtualenv (assuming virtualenvwrapper):
mkvirtualenv -p $(which python3.13) my_virtualenv
install Poetry: https://python-poetry.org/docs/#installation
Install project deps and pre-commit hooks:
poetry install pre-commit install --install-hooks pre-commit run --all-files
If you get weird errors about missing packages from pre-commit try running it with "poetry run pre-commit".
- Branch workflow: feature work goes on
feat/<topic>branches and is merged intomain(via pull request or direct merge); keep the default branchmainfree of direct commits. Wiki doc changes ship ondocs/<topic>branches and must be merged into the wiki'smaster(the wiki UI only rendersmaster; see AGENTS.md). - Ready to go.
Remember to activate your virtualenv whenever working on the repo, this is needed because pylint and mypy pre-commit hooks use the "system" python for now (because reasons).
Running "pre-commit run --all-files" and "py.test -v" regularly during development and especially before committing will save you some headache.