diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..32c88e2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Enter '....'
+3. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - Python version (e.g. `3.11.2`)
+ - Application Version [e.g. `0.0.3-alpha`]
+
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..bbcbbe7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..0a970a4
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,24 @@
+# sandpit Pull Request Template
+
+## Description
+
+Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
+
+Fixes # (issue)
+
+## Testing
+
+Please describe how you have tested your changes.
+
+- [ ] Test A
+- [ ] Test B
+
+## Checklist:
+
+- [ ] My code follows the style guidelines of this project.
+- [ ] I have performed a self-review of my own code.
+- [ ] I have commented my code, particularly in hard-to-understand areas.
+- [ ] I have made corresponding changes to the documentation.
+- [ ] My changes generate no new warnings.
+- [ ] Any dependent changes have been merged and published in downstream modules.
+- [ ] I understand and acknowledge that the code will be published under a BSD 3-Clause license.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..5460f1b
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,39 @@
+name: sandpit
+
+on:
+  push:
+    branches:    
+      - master
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip setuptools wheel pylint pytest pytest-cov coverage
+        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+    - name: Lint with pylint
+      run: |
+        pylint -E -ry sandpit
+ #   - name: Test coverage
+ #     run: |
+ #       python -m coverage run --source=sandpit -m pytest
+ #       python -m coverage report
+    - name: Generate coverage report
+      run: |
+        pytest --cov=sandpit --cov-report=xml
+    - name: "Upload coverage to Codecov"
+      uses: codecov/codecov-action@v3
+      with:
+        fail_ci_if_error: true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..52d4c6a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+.DS_Store
+.dropbox
+.coverage
+*.code-workspace
+.vscode
+/htmlcov
+/build
+/dist
+/docs
+/references
+/src/sandpit.egg*
+/src/sandpit/__pycache__
+/.dbeaver
+*.pyc
+/.pytest_cache/
+/tests/__pycache__
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..84d54c1
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+  and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+  overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+  advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+  address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+semuadmin.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior,  harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..592fc36
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# How to contribute
+
+sandpit is an internal development and testing repo not intended for public consumption, but if you fancy making a contribution, knock yourself out.
+
+## Coding conventions
+
+  * This is open source software. We endeavour to make the code as transparent as possible.
+  * We use Visual Studio Code for development and testing, but you are at liberty to use your preferred IDE.
+  * We document the code in accordance with [Sphinx](https://www.sphinx-doc.org/en/master/) docstring conventions.
+  * We use [pylint](https://pypi.org/project/pylint/) (>=2.6.0) for code analysis.
+  * We use [black](https://pypi.org/project/black/) (>= 20.8) for code formatting and ask that you do the same.
+
+## Testing
+
+We use python's native pytest framework for local unit testing, complemented by the GitHub Actions automated build and testing workflow.
+
+Please write pytest examples for new code you create and add them to the /tests folder following the naming convention test_*.py.
+
+## Submitting changes
+
+Please send a [GitHub Pull Request to sandpit](https://github.com/semuadmin/sandpit/pulls) with a clear list of what you've done (read more about [pull requests](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-pull-requests)). Please follow our coding conventions (below) and make sure all of your commits are atomic (one feature per commit).
+
+Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
+
+    $ git commit -m "A brief summary of the commit
+    > 
+    > A paragraph describing what changed and its impact."
+
+Thanks,
+
+semuadmin
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..739ff24
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2021, SEMU Admin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..32ac059
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+global-exclude __pycache__
+global-exclude *.pyc
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b187dc6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+sandpit
+=======
+
+** NOTHING TO SEE HERE - MOVE ALONG **
+
+Testing sandpit for GitHub development workflows, build toolchains and other shiz.
+
+### Current Status
+
+![Release](https://img.shields.io/github/v/release/semuadmin/sandpit?include_prereleases)
+![Build](https://img.shields.io/github/actions/workflow/status/semuadmin/sandpit/main.yml)
+![Codecov](https://img.shields.io/codecov/c/github/semuadmin/sandpit)
+![Release Date](https://img.shields.io/github/release-date-pre/semuadmin/sandpit)
+![Last Commit](https://img.shields.io/github/last-commit/semuadmin/sandpit)
+![Contributors](https://img.shields.io/github/contributors/semuadmin/sandpit.svg)
+![Open Issues](https://img.shields.io/github/issues-raw/semuadmin/sandpit)
+
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
new file mode 100644
index 0000000..557eaca
--- /dev/null
+++ b/RELEASE_NOTES.md
@@ -0,0 +1,9 @@
+# sandpit Release Notes
+
+### RELEASE 0.0.4
+
+1. Rotten to the core.
+
+### RELEASE 0.0.3
+
+1. None of the talents.
\ No newline at end of file
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..49ca705
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# sandpit Security Policy
+
+## Supported Versions
+
+The following versions are currently being supported with security updates.
+
+![Release](https://img.shields.io/github/v/release/semuadmin/sandpit?include_prereleases)
+
+## Reporting a Vulnerability
+
+Please report any suspected security vulnerabilities via the supplied
+[Issue Template](https://github.com/semuadmin/sandpit/blob/master/.github/ISSUE_TEMPLATE/bug_report.md).
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..22fadf4
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,76 @@
+[build-system]
+requires = ["setuptools>=40.8.0", "wheel"]
+build-backend = "setuptools.build_meta:__legacy__"
+
+[project]
+name = "sandpit"
+authors = [
+    {name = "semuadmin", email = "semuadmin@semuconsulting.com"},
+]
+maintainers = [
+  {name = "semuadmin", email = "semuadmin@semuconsulting.com"}
+]
+description = "Sandpit development, test and build environment"
+version = "0.0.4"
+license = {file = "LICENSE"}
+readme = "README.md"
+requires-python = ">=3.7"
+classifiers = [
+    "License :: OSI Approved :: BSD License",
+    "Operating System :: OS Independent",
+    "Development Status :: 2 - Pre-Alpha",
+    "Environment :: MacOS X",
+    "Environment :: Win32 (MS Windows)",
+    "Environment :: X11 Applications",
+    "Environment :: Console",
+    "Intended Audience :: Developers",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Topic :: System :: Installation/Setup",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+    ]
+
+dependencies = [
+
+]
+
+[project.urls]
+homepage = "https://github.com/semuadmin/sandpit"
+documentation = "https://github.com/semuadmin/sandpit"
+repository = "https://github.com/semuadmin/sandpit"
+changelog = "https://github.com/semuadmin/sandpit/blob/master/RELEASE_NOTES.md"
+
+[project.optional-dependencies]
+test = [
+    "black >= 23.1",
+    "pylint >= 2.17",
+    "pytest >= 7.2",
+    "pytest-cov >= 4.0",
+    "Sphinx >= 6.1.3",
+    "sphinx-rtd-theme >= 1.2.0",
+]
+
+[tool.pylint]
+exit-zero = ""
+disable = """
+    raw-checker-failed,
+    bad-inline-option,
+    locally-disabled,
+    file-ignored,
+    suppressed-message,
+    useless-suppression,
+    deprecated-pragma,
+    use-symbolic-message-instead,
+"""
+
+[tool.pytest.ini_options]
+minversion = "7.0"
+addopts = "--cov --cov-report term-missing --cov-fail-under 95"
+pythonpath = ["src"]
+
+[tool.coverage.run]
+source = ["src"]
diff --git a/src/sandpit/__init__.py b/src/sandpit/__init__.py
new file mode 100644
index 0000000..3e6f968
--- /dev/null
+++ b/src/sandpit/__init__.py
@@ -0,0 +1,10 @@
+"""
+Created on 19 Dec 2020
+
+@author: semuadmin
+"""
+# pylint: disable=invalid-name
+
+from ._version import __version__
+
+version = __version__
diff --git a/src/sandpit/_version.py b/src/sandpit/_version.py
new file mode 100644
index 0000000..52d6020
--- /dev/null
+++ b/src/sandpit/_version.py
@@ -0,0 +1,7 @@
+"""
+Created on 19 Dec 2020
+
+@author: semuadmin
+"""
+
+__version__ = "0.0.4"
diff --git a/src/sandpit/main.py b/src/sandpit/main.py
new file mode 100644
index 0000000..237c755
--- /dev/null
+++ b/src/sandpit/main.py
@@ -0,0 +1,61 @@
+"""
+NOTHING TO SEE HERE - MOVE ALONG
+
+Created on 19 Dec 2020
+
+@author: semuadmin
+"""
+
+from math import sin, cos, acos, atan2, pi, radians
+
+WGS84 = "WGS_84"
+WGS84_SMAJ_AXIS = 6378137.0  # semi-major axis
+WGS84_FLATTENING = 298.257223563  # flattening
+
+
+def haversine(
+    lat1: float,
+    lon1: float,
+    lat2: float,
+    lon2: float,
+    radius: int = WGS84_SMAJ_AXIS / 1000,
+) -> float:
+    """
+    Calculate spherical distance in km between two coordinates using haversine formula.
+    :param float lat1: lat1
+    :param float lon1: lon1
+    :param float lat2: lat2
+    :param float lon2: lon2
+    :param float radius: radius in km (Earth = 6378.137 km)
+    :return: spherical distance in km
+    :rtype: float
+    """
+
+    phi1, lambda1, phi2, lambda2 = [c * pi / 180 for c in (lat1, lon1, lat2, lon2)]
+    dist = radius * acos(
+        cos(phi2 - phi1) - cos(phi1) * cos(phi2) * (1 - cos(lambda2 - lambda1))
+    )
+
+    return round(dist, 3)
+
+
+def bearing(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
+    """
+    Calculate bearing between two coordinates.
+    :param float lat1: lat1 φ1
+    :param float lon1: lon1 λ1
+    :param float lat2: lat2 φ2
+    :param float lon2: lon2 λ2
+    :param float rds: earth radius (6371 km)
+    :return: bearing in degrees
+    :rtype: float
+    """
+
+    coordinates = lat1, lon1, lat2, lon2
+    phi1, lambda1, phi2, lambda2 = [radians(c) for c in coordinates]
+    y = sin(lambda2 - lambda1) * cos(phi2)
+    x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(lambda2 - lambda1)
+    theta = atan2(y, x)
+    brng = (theta * 180 / pi + 360) % 360
+
+    return brng
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..58ecd3b
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,6 @@
+'''
+Created on 19 Dec 2020
+
+@author: semuadmin
+'''
+
diff --git a/tests/test_methods.py b/tests/test_methods.py
new file mode 100644
index 0000000..4a5d589
--- /dev/null
+++ b/tests/test_methods.py
@@ -0,0 +1,39 @@
+"""
+Created on 19 Dec 2020
+
+@author: semuadmin
+"""
+
+import unittest
+from sandpit.main import haversine, bearing
+
+
+class StaticTest(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def testhaversine(self):
+        res = haversine(51.23, -2.41, 34.205, 56.34)
+        self.assertAlmostEqual(res, 5010.722, 3)
+        res = haversine(-12.645, 34.867, 145.1745, -56.27846)
+        self.assertAlmostEqual(res, 10715.371, 3)
+        res = haversine(53.45, -2.14, 53.451, -2.141)
+        self.assertAlmostEqual(res, 0.1296, 3)
+
+    def testbearing(self):
+        res = bearing(51.23, -2.41, 53.205, -2.34)
+        self.assertAlmostEqual(res, 1.216362703824359, 4)
+        res = bearing(51.23145, -2.41, 51.23145, -2.34)
+        self.assertAlmostEqual(res, 89.9727111358776, 4)
+        res = bearing(51.23, -2.41, 34.205, 56.34)
+        self.assertAlmostEqual(res, 88.58134073451902, 4)
+        res = bearing(-12.645, 34.867, -34.1745, 48.27846)
+        self.assertAlmostEqual(res, 152.70835788275326, 4)
+
+
+if __name__ == "__main__":
+    # import sys;sys.argv = ['', 'Test.testName']
+    unittest.main()