summary refs log tree commit diff
path: root/vm
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-12-12 00:22:32 +0000
committerAlyssa Ross <hi@alyssa.is>2021-12-12 00:22:32 +0000
commite0b8edf24020f7dd43a90eae8b00754554bb6768 (patch)
tree65348902fcb7167567dc8ccbab1a093446d7e5df /vm
parent70198ad12e62739fac181f7487ae2f269425631c (diff)
downloadspectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.tar
spectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.tar.gz
spectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.tar.bz2
spectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.tar.lz
spectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.tar.xz
spectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.tar.zst
spectrum-e0b8edf24020f7dd43a90eae8b00754554bb6768.zip
vm/app/catgirl: move to monorepo path
Diffstat (limited to 'vm')
-rw-r--r--vm/app/catgirl/.gitignore6
-rw-r--r--vm/app/catgirl/LICENSES/CC0-1.0.txt121
-rw-r--r--vm/app/catgirl/LICENSES/EUPL-1.2.txt287
-rw-r--r--vm/app/catgirl/Makefile130
l---------vm/app/catgirl/bin1
-rw-r--r--vm/app/catgirl/default.nix85
-rw-r--r--vm/app/catgirl/etc/fstab4
-rwxr-xr-xvm/app/catgirl/etc/init27
-rw-r--r--vm/app/catgirl/etc/mdev.conf5
-rwxr-xr-xvm/app/catgirl/etc/mdev/iface36
-rw-r--r--vm/app/catgirl/etc/passwd1
-rw-r--r--vm/app/catgirl/etc/passwd.license2
-rw-r--r--vm/app/catgirl/etc/resolv.conf4
-rwxr-xr-xvm/app/catgirl/etc/s6-rc/catgirl/run31
-rw-r--r--vm/app/catgirl/etc/s6-rc/catgirl/type1
-rw-r--r--vm/app/catgirl/etc/s6-rc/catgirl/type.license2
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd-coldplug/dependencies4
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type1
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type.license2
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd-coldplug/up4
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd/notification-fd1
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd/notification-fd.license2
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd/run5
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd/type1
-rw-r--r--vm/app/catgirl/etc/s6-rc/mdevd/type.license2
-rw-r--r--vm/app/catgirl/etc/s6-rc/ok-all/contents4
-rw-r--r--vm/app/catgirl/etc/s6-rc/ok-all/type1
-rw-r--r--vm/app/catgirl/etc/s6-rc/ok-all/type.license2
l---------vm/app/catgirl/etc/ssl/certs/ca-certificates.crt1
-rwxr-xr-xvm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/data/pid2mac21
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/dependencies4
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd1
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd.license2
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/run60
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type1
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type.license2
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl/dependencies4
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl/run13
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl/type1
-rw-r--r--vm/app/catgirl/host/s6-rc/appvm-catgirl/type.license2
-rw-r--r--vm/app/catgirl/shell.nix17
41 files changed, 901 insertions, 0 deletions
diff --git a/vm/app/catgirl/.gitignore b/vm/app/catgirl/.gitignore
new file mode 100644
index 0000000..2849946
--- /dev/null
+++ b/vm/app/catgirl/.gitignore
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+/build
+result
+result-*
diff --git a/vm/app/catgirl/LICENSES/CC0-1.0.txt b/vm/app/catgirl/LICENSES/CC0-1.0.txt
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/vm/app/catgirl/LICENSES/CC0-1.0.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/vm/app/catgirl/LICENSES/EUPL-1.2.txt b/vm/app/catgirl/LICENSES/EUPL-1.2.txt
new file mode 100644
index 0000000..4153cd3
--- /dev/null
+++ b/vm/app/catgirl/LICENSES/EUPL-1.2.txt
@@ -0,0 +1,287 @@
+                      EUROPEAN UNION PUBLIC LICENCE v. 1.2
+                      EUPL © the European Union 2007, 2016
+
+This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
+below) which is provided under the terms of this Licence. Any use of the Work,
+other than as authorised under this Licence is prohibited (to the extent such
+use is covered by a right of the copyright holder of the Work).
+
+The Work is provided under the terms of this Licence when the Licensor (as
+defined below) has placed the following notice immediately following the
+copyright notice for the Work:
+
+        Licensed under the EUPL
+
+or has expressed by any other means his willingness to license under the EUPL.
+
+1. Definitions
+
+In this Licence, the following terms have the following meaning:
+
+- ‘The Licence’: this Licence.
+
+- ‘The Original Work’: the work or software distributed or communicated by the
+  Licensor under this Licence, available as Source Code and also as Executable
+  Code as the case may be.
+
+- ‘Derivative Works’: the works or software that could be created by the
+  Licensee, based upon the Original Work or modifications thereof. This Licence
+  does not define the extent of modification or dependence on the Original Work
+  required in order to classify a work as a Derivative Work; this extent is
+  determined by copyright law applicable in the country mentioned in Article 15.
+
+- ‘The Work’: the Original Work or its Derivative Works.
+
+- ‘The Source Code’: the human-readable form of the Work which is the most
+  convenient for people to study and modify.
+
+- ‘The Executable Code’: any code which has generally been compiled and which is
+  meant to be interpreted by a computer as a program.
+
+- ‘The Licensor’: the natural or legal person that distributes or communicates
+  the Work under the Licence.
+
+- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
+  Licence, or otherwise contributes to the creation of a Derivative Work.
+
+- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
+  the Work under the terms of the Licence.
+
+- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
+  renting, distributing, communicating, transmitting, or otherwise making
+  available, online or offline, copies of the Work or providing access to its
+  essential functionalities at the disposal of any other natural or legal
+  person.
+
+2. Scope of the rights granted by the Licence
+
+The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+sublicensable licence to do the following, for the duration of copyright vested
+in the Original Work:
+
+- use the Work in any circumstance and for all usage,
+- reproduce the Work,
+- modify the Work, and make Derivative Works based upon the Work,
+- communicate to the public, including the right to make available or display
+  the Work or copies thereof to the public and perform publicly, as the case may
+  be, the Work,
+- distribute the Work or copies thereof,
+- lend and rent the Work or copies thereof,
+- sublicense rights in the Work or copies thereof.
+
+Those rights can be exercised on any media, supports and formats, whether now
+known or later invented, as far as the applicable law permits so.
+
+In the countries where moral rights apply, the Licensor waives his right to
+exercise his moral right to the extent allowed by law in order to make effective
+the licence of the economic rights here above listed.
+
+The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
+any patents held by the Licensor, to the extent necessary to make use of the
+rights granted on the Work under this Licence.
+
+3. Communication of the Source Code
+
+The Licensor may provide the Work either in its Source Code form, or as
+Executable Code. If the Work is provided as Executable Code, the Licensor
+provides in addition a machine-readable copy of the Source Code of the Work
+along with each copy of the Work that the Licensor distributes or indicates, in
+a notice following the copyright notice attached to the Work, a repository where
+the Source Code is easily and freely accessible for as long as the Licensor
+continues to distribute or communicate the Work.
+
+4. Limitations on copyright
+
+Nothing in this Licence is intended to deprive the Licensee of the benefits from
+any exception or limitation to the exclusive rights of the rights owners in the
+Work, of the exhaustion of those rights or of other applicable limitations
+thereto.
+
+5. Obligations of the Licensee
+
+The grant of the rights mentioned above is subject to some restrictions and
+obligations imposed on the Licensee. Those obligations are the following:
+
+Attribution right: The Licensee shall keep intact all copyright, patent or
+trademarks notices and all notices that refer to the Licence and to the
+disclaimer of warranties. The Licensee must include a copy of such notices and a
+copy of the Licence with every copy of the Work he/she distributes or
+communicates. The Licensee must cause any Derivative Work to carry prominent
+notices stating that the Work has been modified and the date of modification.
+
+Copyleft clause: If the Licensee distributes or communicates copies of the
+Original Works or Derivative Works, this Distribution or Communication will be
+done under the terms of this Licence or of a later version of this Licence
+unless the Original Work is expressly distributed only under this version of the
+Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
+(becoming Licensor) cannot offer or impose any additional terms or conditions on
+the Work or Derivative Work that alter or restrict the terms of the Licence.
+
+Compatibility clause: If the Licensee Distributes or Communicates Derivative
+Works or copies thereof based upon both the Work and another work licensed under
+a Compatible Licence, this Distribution or Communication can be done under the
+terms of this Compatible Licence. For the sake of this clause, ‘Compatible
+Licence’ refers to the licences listed in the appendix attached to this Licence.
+Should the Licensee's obligations under the Compatible Licence conflict with
+his/her obligations under this Licence, the obligations of the Compatible
+Licence shall prevail.
+
+Provision of Source Code: When distributing or communicating copies of the Work,
+the Licensee will provide a machine-readable copy of the Source Code or indicate
+a repository where this Source will be easily and freely available for as long
+as the Licensee continues to distribute or communicate the Work.
+
+Legal Protection: This Licence does not grant permission to use the trade names,
+trademarks, service marks, or names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the copyright notice.
+
+6. Chain of Authorship
+
+The original Licensor warrants that the copyright in the Original Work granted
+hereunder is owned by him/her or licensed to him/her and that he/she has the
+power and authority to grant the Licence.
+
+Each Contributor warrants that the copyright in the modifications he/she brings
+to the Work are owned by him/her or licensed to him/her and that he/she has the
+power and authority to grant the Licence.
+
+Each time You accept the Licence, the original Licensor and subsequent
+Contributors grant You a licence to their contributions to the Work, under the
+terms of this Licence.
+
+7. Disclaimer of Warranty
+
+The Work is a work in progress, which is continuously improved by numerous
+Contributors. It is not a finished work and may therefore contain defects or
+‘bugs’ inherent to this type of development.
+
+For the above reason, the Work is provided under the Licence on an ‘as is’ basis
+and without warranties of any kind concerning the Work, including without
+limitation merchantability, fitness for a particular purpose, absence of defects
+or errors, accuracy, non-infringement of intellectual property rights other than
+copyright as stated in Article 6 of this Licence.
+
+This disclaimer of warranty is an essential part of the Licence and a condition
+for the grant of any rights to the Work.
+
+8. Disclaimer of Liability
+
+Except in the cases of wilful misconduct or damages directly caused to natural
+persons, the Licensor will in no event be liable for any direct or indirect,
+material or moral, damages of any kind, arising out of the Licence or of the use
+of the Work, including without limitation, damages for loss of goodwill, work
+stoppage, computer failure or malfunction, loss of data or any commercial
+damage, even if the Licensor has been advised of the possibility of such damage.
+However, the Licensor will be liable under statutory product liability laws as
+far such laws apply to the Work.
+
+9. Additional agreements
+
+While distributing the Work, You may choose to conclude an additional agreement,
+defining obligations or services consistent with this Licence. However, if
+accepting obligations, You may act only on your own behalf and on your sole
+responsibility, not on behalf of the original Licensor or any other Contributor,
+and only if You agree to indemnify, defend, and hold each Contributor harmless
+for any liability incurred by, or claims asserted against such Contributor by
+the fact You have accepted any warranty or additional liability.
+
+10. Acceptance of the Licence
+
+The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
+placed under the bottom of a window displaying the text of this Licence or by
+affirming consent in any other similar way, in accordance with the rules of
+applicable law. Clicking on that icon indicates your clear and irrevocable
+acceptance of this Licence and all of its terms and conditions.
+
+Similarly, you irrevocably accept this Licence and all of its terms and
+conditions by exercising any rights granted to You by Article 2 of this Licence,
+such as the use of the Work, the creation by You of a Derivative Work or the
+Distribution or Communication by You of the Work or copies thereof.
+
+11. Information to the public
+
+In case of any Distribution or Communication of the Work by means of electronic
+communication by You (for example, by offering to download the Work from a
+remote location) the distribution channel or media (for example, a website) must
+at least provide to the public the information requested by the applicable law
+regarding the Licensor, the Licence and the way it may be accessible, concluded,
+stored and reproduced by the Licensee.
+
+12. Termination of the Licence
+
+The Licence and the rights granted hereunder will terminate automatically upon
+any breach by the Licensee of the terms of the Licence.
+
+Such a termination will not terminate the licences of any person who has
+received the Work from the Licensee under the Licence, provided such persons
+remain in full compliance with the Licence.
+
+13. Miscellaneous
+
+Without prejudice of Article 9 above, the Licence represents the complete
+agreement between the Parties as to the Work.
+
+If any provision of the Licence is invalid or unenforceable under applicable
+law, this will not affect the validity or enforceability of the Licence as a
+whole. Such provision will be construed or reformed so as necessary to make it
+valid and enforceable.
+
+The European Commission may publish other linguistic versions or new versions of
+this Licence or updated versions of the Appendix, so far this is required and
+reasonable, without reducing the scope of the rights granted by the Licence. New
+versions of the Licence will be published with a unique version number.
+
+All linguistic versions of this Licence, approved by the European Commission,
+have identical value. Parties can take advantage of the linguistic version of
+their choice.
+
+14. Jurisdiction
+
+Without prejudice to specific agreement between parties,
+
+- any litigation resulting from the interpretation of this License, arising
+  between the European Union institutions, bodies, offices or agencies, as a
+  Licensor, and any Licensee, will be subject to the jurisdiction of the Court
+  of Justice of the European Union, as laid down in article 272 of the Treaty on
+  the Functioning of the European Union,
+
+- any litigation arising between other parties and resulting from the
+  interpretation of this License, will be subject to the exclusive jurisdiction
+  of the competent court where the Licensor resides or conducts its primary
+  business.
+
+15. Applicable Law
+
+Without prejudice to specific agreement between parties,
+
+- this Licence shall be governed by the law of the European Union Member State
+  where the Licensor has his seat, resides or has his registered office,
+
+- this licence shall be governed by Belgian law if the Licensor has no seat,
+  residence or registered office inside a European Union Member State.
+
+Appendix
+
+‘Compatible Licences’ according to Article 5 EUPL are:
+
+- GNU General Public License (GPL) v. 2, v. 3
+- GNU Affero General Public License (AGPL) v. 3
+- Open Software License (OSL) v. 2.1, v. 3.0
+- Eclipse Public License (EPL) v. 1.0
+- CeCILL v. 2.0, v. 2.1
+- Mozilla Public Licence (MPL) v. 2
+- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
+- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
+  works other than software
+- European Union Public Licence (EUPL) v. 1.1, v. 1.2
+- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
+  Reciprocity (LiLiQ-R+).
+
+The European Commission may update this Appendix to later versions of the above
+licences without producing a new version of the EUPL, as long as they provide
+the rights granted in Article 2 of this Licence and protect the covered Source
+Code from exclusive appropriation.
+
+All other changes or additions to this Appendix require the production of a new
+EUPL version.
diff --git a/vm/app/catgirl/Makefile b/vm/app/catgirl/Makefile
new file mode 100644
index 0000000..586a443
--- /dev/null
+++ b/vm/app/catgirl/Makefile
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+# qemu-kvm is non-standard, but is present in at least Fedora and
+# Nixpkgs.  If you don't have qemu-kvm, you'll need to set e.g.
+# QEMU_KVM = qemu-system-x86_64 -enable-kvm.
+QEMU_KVM = qemu-kvm
+CLOUD_HYPERVISOR = cloud-hypervisor
+
+VMM = qemu
+
+# These don't have the host/ prefix because they're not referring to
+# paths in the source tree.
+HOST_S6_RC_DIRECTORIES = s6-rc/appvm-catgirl-vmm/env
+
+HOST_S6_RC_FILES = \
+	host/s6-rc/appvm-catgirl-vmm/data/pid2mac \
+	host/s6-rc/appvm-catgirl-vmm/dependencies \
+	host/s6-rc/appvm-catgirl-vmm/notification-fd \
+	host/s6-rc/appvm-catgirl-vmm/run \
+	host/s6-rc/appvm-catgirl-vmm/type \
+	host/s6-rc/appvm-catgirl/dependencies \
+	host/s6-rc/appvm-catgirl/run \
+	host/s6-rc/appvm-catgirl/type
+
+HOST_S6_RC_BUILD_FILES = \
+	build/host/data/appvm-catgirl/rootfs.ext4 \
+	build/host/data/appvm-catgirl/vmlinux
+
+# We produce an s6-rc source directory, but that doesn't play nice
+# with Make, because it won't know to update if some file in the
+# directory is changed, or a file is created or removed in a
+# subdirectory.  Using the whole source directory could also end up
+# including files that aren't intended to be part of the input, like
+# temporary editor files or .license files.  So for all these reasons,
+# only explicitly listed files are included in the build result.
+build/svc: $(HOST_S6_RC_FILES) $(HOST_S6_RC_BUILD_FILES)
+	rm -rf $@
+	mkdir -p $@
+
+	tar -c $(HOST_S6_RC_FILES) | tar -C $@ -x --strip-components 1
+	tar -c $(HOST_S6_RC_BUILD_FILES) | tar -C $@ -x --strip-components 2
+	cd $@ && mkdir -p $(HOST_S6_RC_DIRECTORIES)
+
+build/host/data/appvm-catgirl/vmlinux: $(VMLINUX)
+	mkdir -p $$(dirname $@)
+	cp $(VMLINUX) $@
+
+# tar2ext4 will leave half a filesystem behind if it's interrupted
+# half way through.
+build/host/data/appvm-catgirl/rootfs.ext4: build/rootfs.tar
+	mkdir -p $$(dirname $@)
+	tar2ext4 -i build/rootfs.tar -o $@.tmp
+	mv $@.tmp $@
+
+VM_FILES = \
+	etc/fstab \
+	etc/init \
+	etc/mdev.conf \
+	etc/mdev/iface \
+	etc/passwd \
+	etc/resolv.conf
+
+# These are separate because they need to be included, but putting
+# them as make dependencies would confuse make.
+VM_LINKS = bin etc/ssl/certs/ca-certificates.crt
+
+VM_BUILD_FILES = build/etc/s6-rc
+VM_MOUNTPOINTS = dev run proc sys
+
+build/rootfs.tar: $(PACKAGES_TAR) $(VM_FILES) $(VM_BUILD_FILES)
+	cp --no-preserve=mode -f $(PACKAGES_TAR) $@
+	tar $(TARFLAGS) --append -f $@ $(VM_FILES) $(VM_LINKS)
+	echo $(VM_BUILD_FILES) | cut -d/ -f2 | \
+	    tar $(TARFLAGS) --append -f $@ -C build -T -
+	for m in $(VM_MOUNTPOINTS); do \
+	    tar $(TARFLAGS) --append -hf $@ --xform="s,.*,$$m," /var/empty ; \
+	done
+	tar $(TARFLAGS) --append -hf $@ --xform='s,.*,etc/service,' /var/empty
+
+VM_S6_RC_FILES = \
+	etc/s6-rc/catgirl/run \
+	etc/s6-rc/catgirl/type \
+	etc/s6-rc/mdevd-coldplug/dependencies \
+	etc/s6-rc/mdevd-coldplug/type \
+	etc/s6-rc/mdevd-coldplug/up \
+	etc/s6-rc/mdevd/notification-fd \
+	etc/s6-rc/mdevd/run \
+	etc/s6-rc/mdevd/type \
+	etc/s6-rc/ok-all/contents \
+	etc/s6-rc/ok-all/type
+
+build/etc/s6-rc: $(VM_S6_RC_FILES)
+	mkdir -p $$(dirname $@)
+	rm -rf $@
+
+	dir=$$(mktemp -d) && \
+	    tar -c $(VM_S6_RC_FILES) | tar -C $$dir -x --strip-components 2 && \
+	    s6-rc-compile $@ $$dir; \
+	    exit=$$?; rm -r $$dir; exit $$exit
+
+run-qemu: build/host/data/appvm-catgirl/rootfs.ext4
+	$(QEMU_KVM) -m 128 -cpu host -machine q35,kernel=$(KERNEL) -vga none \
+	  -drive file=build/host/data/appvm-catgirl/rootfs.ext4,if=virtio,format=raw,readonly=on \
+	  -append "console=ttyS0 root=/dev/vda" \
+	  -netdev user,id=net0 \
+	  -device virtio-net,netdev=net0,mac=0A:B3:EC:00:00:00 \
+	  -chardev pty,id=virtiocon0 \
+	  -device virtio-serial-pci \
+	  -device virtconsole,chardev=virtiocon0
+.PHONY: run-qemu
+
+run-cloud-hypervisor: build/host/data/appvm-catgirl/rootfs.ext4
+	$(CLOUD_HYPERVISOR) \
+	    --api-socket path=vmm.sock \
+	    --memory size=128M
+	    --disk path=build/host/data/appvm-catgirl/rootfs.ext4,readonly=on \
+	    --net tap=tap0,mac=0A:B3:EC:00:00:00 \
+	    --kernel $(KERNEL) \
+	    --cmdline "console=ttyS0 root=/dev/vda" \
+	    --console pty \
+	    --serial tty
+.PHONY: run-cloud-hypervisor
+
+run: run-$(VMM)
+.PHONY: run
+
+clean:
+	rm -rf build
+.PHONY: clean
diff --git a/vm/app/catgirl/bin b/vm/app/catgirl/bin
new file mode 120000
index 0000000..1e881ed
--- /dev/null
+++ b/vm/app/catgirl/bin
@@ -0,0 +1 @@
+usr/bin
\ No newline at end of file
diff --git a/vm/app/catgirl/default.nix b/vm/app/catgirl/default.nix
new file mode 100644
index 0000000..3d098d0
--- /dev/null
+++ b/vm/app/catgirl/default.nix
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+{ pkgs ? import <nixpkgs> {}
+, terminfo ? pkgs.foot.terminfo
+}:
+
+pkgs.pkgsStatic.callPackage (
+
+{ lib, stdenv, runCommand, writeReferencesToFile, buildPackages
+, s6-rc, tar2ext4
+, busybox, cacert, catgirl, execline, linux, mdevd, s6, s6-linux-utils
+, s6-portable-utils
+}:
+
+let
+  inherit (lib) cleanSource cleanSourceWith concatMapStringsSep;
+
+  packages = [
+    busybox catgirl execline mdevd s6 s6-linux-utils s6-portable-utils s6-rc
+  ];
+
+  packagesSysroot = runCommand "packages-sysroot" {
+    inherit packages;
+    passAsFile = [ "packages" ];
+  } ''
+    mkdir -p $out/usr/bin $out/usr/share
+    ln -s ${concatMapStringsSep " " (p: "${p}/bin/*") packages} $out/usr/bin
+    ln -s ${kernel}/lib "$out"
+    ln -s ${terminfo}/share/terminfo $out/usr/share
+    ln -s ${cacert}/etc/ssl $out/usr/share
+  '';
+
+  packagesTar = runCommand "packages.tar" {} ''
+    cd ${packagesSysroot}
+    tar -cf $out --verbatim-files-from \
+        -T ${writeReferencesToFile packagesSysroot} .
+  '';
+
+  kernel = buildPackages.linux.override {
+    structuredExtraConfig = with lib.kernel; {
+      VIRTIO = yes;
+      VIRTIO_PCI = yes;
+      VIRTIO_BLK = yes;
+      VIRTIO_CONSOLE = yes;
+      EXT4_FS = yes;
+      DRM_BOCHS = yes;
+      DRM = yes;
+      AGP = yes;
+    };
+  };
+in
+
+stdenv.mkDerivation {
+  name = "spectrum-appvm-catgirl";
+
+  src = cleanSourceWith {
+    filter = name: _type: name != "${toString ./.}/build";
+    src = cleanSource ./.;
+  };
+
+  nativeBuildInputs = [ s6-rc tar2ext4 ];
+
+  PACKAGES_TAR = packagesTar;
+  VMLINUX = "${kernel.dev}/vmlinux";
+
+  postPatch = ''
+    mkdir $NIX_BUILD_TOP/empty
+    substituteInPlace Makefile --replace /var/empty $NIX_BUILD_TOP/empty
+  '';
+
+  installPhase = ''
+    mv build/svc $out
+  '';
+
+  enableParallelBuilding = true;
+
+  passthru = { inherit kernel; };
+
+  meta = with lib; {
+    license = licenses.eupl12;
+    platforms = platforms.linux;
+  };
+}
+) {}
diff --git a/vm/app/catgirl/etc/fstab b/vm/app/catgirl/etc/fstab
new file mode 100644
index 0000000..ceb1087
--- /dev/null
+++ b/vm/app/catgirl/etc/fstab
@@ -0,0 +1,4 @@
+proc	/proc		proc	defaults
+devpts	/dev/pts	devpts	defaults,gid=4,mode=620
+tmpfs	/dev/shm	tmpfs	defaults
+sysfs	/sys		sysfs	defaults
diff --git a/vm/app/catgirl/etc/init b/vm/app/catgirl/etc/init
new file mode 100755
index 0000000..e144760
--- /dev/null
+++ b/vm/app/catgirl/etc/init
@@ -0,0 +1,27 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+
+/bin/export PATH /bin
+/bin/s6-setsid -qb --
+
+umask 022
+if { s6-mount -t tmpfs -o mode=0755 tmpfs /run }
+if { s6-hiercopy /etc/service /run/service }
+emptyenv -p
+
+background {
+  s6-setsid --
+
+  if { s6-rc-init -c /etc/s6-rc /run/service }
+
+  if { s6-mkdir -p /dev/pts /dev/shm }
+
+  if { s6-mount -a }
+
+  s6-rc change ok-all
+}
+
+unexport !
+cd /run/service
+s6-svscan
diff --git a/vm/app/catgirl/etc/mdev.conf b/vm/app/catgirl/etc/mdev.conf
new file mode 100644
index 0000000..d441c68
--- /dev/null
+++ b/vm/app/catgirl/etc/mdev.conf
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+-$MODALIAS=.* 0:0 660 +importas -iu MODALIAS MODALIAS modprobe -q $MODALIAS
+$INTERFACE=.* 0:0 660 ! +/etc/mdev/iface
diff --git a/vm/app/catgirl/etc/mdev/iface b/vm/app/catgirl/etc/mdev/iface
new file mode 100755
index 0000000..d711779
--- /dev/null
+++ b/vm/app/catgirl/etc/mdev/iface
@@ -0,0 +1,36 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+
+importas -i INTERFACE INTERFACE
+
+if { test $INTERFACE != lo }
+
+# Our IP is encoded in the NIC-specific portion of the interface's MAC
+# address.
+backtick -E LOCAL_IP {
+  awk -F: "{printf \"100.64.%d.%d\\n\", \"0x\" $5, \"0x\" $6}"
+  /sys/class/net/${INTERFACE}/address
+}
+
+if { ip address add ${LOCAL_IP}/32 dev $INTERFACE }
+if { ip link set $INTERFACE up }
+if { ip route add 169.254.0.1 dev $INTERFACE }
+if { ip route add default via 169.254.0.1 dev $INTERFACE }
+
+# Try to wait for the network to be up.
+# If we time out, well, there's not much we can do, so just carry on.
+# In future, it would be better if the network VM notified us about
+# network changes.
+foreground { printf "Waiting for network… " }
+foreground {
+  ifte { echo "Connected." } { echo "Timed out." }
+  pipeline { seq 10 }
+  forstdin _
+  if -n {
+    redirfd -w 2 /dev/null
+    wget -qT 6 -O /dev/null http://ipv4.connman.net/online/status.html
+  }
+}
+
+s6-rc -u change catgirl
diff --git a/vm/app/catgirl/etc/passwd b/vm/app/catgirl/etc/passwd
new file mode 100644
index 0000000..29f3b25
--- /dev/null
+++ b/vm/app/catgirl/etc/passwd
@@ -0,0 +1 @@
+root:x:0:0:System administrator:/:/bin/sh
diff --git a/vm/app/catgirl/etc/passwd.license b/vm/app/catgirl/etc/passwd.license
new file mode 100644
index 0000000..2b3b032
--- /dev/null
+++ b/vm/app/catgirl/etc/passwd.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/etc/resolv.conf b/vm/app/catgirl/etc/resolv.conf
new file mode 100644
index 0000000..7fcdf3a
--- /dev/null
+++ b/vm/app/catgirl/etc/resolv.conf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+nameserver 1.1.1.1
diff --git a/vm/app/catgirl/etc/s6-rc/catgirl/run b/vm/app/catgirl/etc/s6-rc/catgirl/run
new file mode 100755
index 0000000..c22c2ed
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/catgirl/run
@@ -0,0 +1,31 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+export TERM foot
+export TERMINFO_DIRS /usr/share/terminfo
+export TMPDIR /run
+
+backtick USER { id -un }
+backtick HOME {
+  importas -i user USER
+  homeof $user
+}
+
+importas -i home HOME
+cd $home
+
+redirfd -u 0 /dev/hvc0
+fdmove -c 1 0
+fdmove -c 2 0
+
+foreground { clear }
+unexport ?
+
+# Run catgirl, then a login shell to allow for debugging.
+foreground { printf "IRC nick (to join #spectrum): " }
+foreground {
+  backtick -E nick { head -1 }
+  catgirl -h irc.libera.chat -j "#spectrum-test" -n $nick
+}
+exec -l sh
diff --git a/vm/app/catgirl/etc/s6-rc/catgirl/type b/vm/app/catgirl/etc/s6-rc/catgirl/type
new file mode 100644
index 0000000..5883cff
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/catgirl/type
@@ -0,0 +1 @@
+longrun
diff --git a/vm/app/catgirl/etc/s6-rc/catgirl/type.license b/vm/app/catgirl/etc/s6-rc/catgirl/type.license
new file mode 100644
index 0000000..c49c11b
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/catgirl/type.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/dependencies b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/dependencies
new file mode 100644
index 0000000..59b02b7
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/dependencies
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
+#
+mdevd
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type
new file mode 100644
index 0000000..bdd22a1
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type
@@ -0,0 +1 @@
+oneshot
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type.license b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type.license
new file mode 100644
index 0000000..2b3b032
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/up b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/up
new file mode 100644
index 0000000..a5d4dc1
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/up
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+
+mdevd-coldplug
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd b/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd
@@ -0,0 +1 @@
+3
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd.license b/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd.license
new file mode 100644
index 0000000..2b3b032
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/run b/vm/app/catgirl/etc/s6-rc/mdevd/run
new file mode 100644
index 0000000..03ad5e3
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd/run
@@ -0,0 +1,5 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+
+mdevd -D3
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/type b/vm/app/catgirl/etc/s6-rc/mdevd/type
new file mode 100644
index 0000000..5883cff
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd/type
@@ -0,0 +1 @@
+longrun
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/type.license b/vm/app/catgirl/etc/s6-rc/mdevd/type.license
new file mode 100644
index 0000000..2b3b032
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/mdevd/type.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/etc/s6-rc/ok-all/contents b/vm/app/catgirl/etc/s6-rc/ok-all/contents
new file mode 100644
index 0000000..c4ea84f
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/ok-all/contents
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+#
+mdevd-coldplug
diff --git a/vm/app/catgirl/etc/s6-rc/ok-all/type b/vm/app/catgirl/etc/s6-rc/ok-all/type
new file mode 100644
index 0000000..757b422
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/ok-all/type
@@ -0,0 +1 @@
+bundle
diff --git a/vm/app/catgirl/etc/s6-rc/ok-all/type.license b/vm/app/catgirl/etc/s6-rc/ok-all/type.license
new file mode 100644
index 0000000..c49c11b
--- /dev/null
+++ b/vm/app/catgirl/etc/s6-rc/ok-all/type.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/etc/ssl/certs/ca-certificates.crt b/vm/app/catgirl/etc/ssl/certs/ca-certificates.crt
new file mode 120000
index 0000000..42d8e23
--- /dev/null
+++ b/vm/app/catgirl/etc/ssl/certs/ca-certificates.crt
@@ -0,0 +1 @@
+/usr/share/ssl/certs/ca-bundle.crt
\ No newline at end of file
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/data/pid2mac b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/data/pid2mac
new file mode 100755
index 0000000..862c024
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/data/pid2mac
@@ -0,0 +1,21 @@
+#!/bin/awk -f
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+#
+# Calculate the MACs for our TAP and the router's TAP, based on the
+# pid of the main VMM process to ensure uniqueness.  MAC address
+# format, by octet:
+#
+#  0-3  Static OUI for Spectrum
+#    4  Most significant bit is used to differentiate routers from
+#       clients.  Other bits are reserved.
+#  5-6  Last two octets of client's IP (in 100.64.0.0/16).
+#
+# Parameters:
+# 	-v pid=PID	pid of the main VMM process
+# 	-v octet4=VAL	hex value to be used as octet 4 in the address
+
+BEGIN {
+  hex = sprintf("%.4x", pid);
+  print "0A:B3:EC:" octet4 ":" substr(hex, 0, 2) ":" substr(hex, 3);
+}
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/dependencies b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/dependencies
new file mode 100644
index 0000000..2756452
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/dependencies
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+#
+netvm
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd
@@ -0,0 +1 @@
+3
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd.license b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd.license
new file mode 100644
index 0000000..c49c11b
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/notification-fd.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/run b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/run
new file mode 100644
index 0000000..378f47b
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/run
@@ -0,0 +1,60 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
+
+redirfd -r 0 /dev/null
+
+getpid -E pid
+
+# Checking the return value of the bridge creation is important,
+# because if it fails due to the bridge already existing that means
+# something else could already be using this bridge.
+if { ip link add name br${pid} type bridge }
+background { ip link set br${pid} up }
+
+# Create the net VM end, and attach it to the net VM.
+#
+# TODO: it would be nice if we could have this be non-persistent, so
+# it didn't stick around if the network VM was shut down.
+if {
+  # Try to delete the device in case the VM was powered off (as the
+  # finish script wouldn't have been run in that case.)  Since we check
+  # the return value of ip tuntap add, in the case of a race condition
+  # between deleting the device and creating it again, we'll just fail
+  # and try again.
+  foreground {
+    redirfd -w 2 /dev/null
+    ip link delete router${pid}
+  }
+
+  if { tunctl -t router${pid} }
+  background { ip link set router${pid} master br${pid} }
+  background { ip link set router${pid} up }
+
+  backtick -E router_mac { data/pid2mac -v pid=${pid} -v octet4=80 }
+  curl -Sfso /dev/null
+    -X PUT
+    -H "Content-Type: application/json"
+    -A ""
+    -d "{\"tap\":\"router${pid}\",\"mac\":\"${router_mac}\"}"
+    --unix-socket ../netvm-vmm/env/cloud-hypervisor.sock
+    http://localhost/api/v1/vm.add-net
+}
+
+mktuntap -pvBi client${pid} 4
+background { ip link set client${pid} master br${pid} }
+background { ip link set client${pid} up }
+
+backtick -E client_mac { data/pid2mac -v pid=${pid} -v octet4=00 }
+
+s6-notifyoncheck -dc "test -S env/cloud-hypervisor.sock"
+
+cloud-hypervisor
+  --api-socket env/cloud-hypervisor.sock
+  --kernel /ext/svc/data/appvm-catgirl/vmlinux
+  --cmdline "console=ttyS0 root=/dev/vda"
+  --memory size=128M
+  --disk path=/ext/svc/data/appvm-catgirl/rootfs.ext4,readonly=on
+  --net fd=4,mac=${client_mac}
+  --console pty
+  --serial file=/run/appvm-catgirl.log
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type
new file mode 100644
index 0000000..5883cff
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type
@@ -0,0 +1 @@
+longrun
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type.license b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type.license
new file mode 100644
index 0000000..c49c11b
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl-vmm/type.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl/dependencies b/vm/app/catgirl/host/s6-rc/appvm-catgirl/dependencies
new file mode 100644
index 0000000..3804b93
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl/dependencies
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+#
+appvm-catgirl-vmm
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl/run b/vm/app/catgirl/host/s6-rc/appvm-catgirl/run
new file mode 100644
index 0000000..01560e3
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl/run
@@ -0,0 +1,13 @@
+#!/bin/execlineb -P
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+export WAYLAND_DISPLAY wayland-1
+export XDG_RUNTIME_DIR /run/user/0
+
+backtick -E pty {
+  pipeline -w { jq -r .config.console.file }
+  ch-remote --api-socket ../appvm-catgirl-vmm/env/cloud-hypervisor.sock info
+}
+
+foot -T appvm-catgirl --pty $pty
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl/type b/vm/app/catgirl/host/s6-rc/appvm-catgirl/type
new file mode 100644
index 0000000..5883cff
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl/type
@@ -0,0 +1 @@
+longrun
diff --git a/vm/app/catgirl/host/s6-rc/appvm-catgirl/type.license b/vm/app/catgirl/host/s6-rc/appvm-catgirl/type.license
new file mode 100644
index 0000000..c49c11b
--- /dev/null
+++ b/vm/app/catgirl/host/s6-rc/appvm-catgirl/type.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/catgirl/shell.nix b/vm/app/catgirl/shell.nix
new file mode 100644
index 0000000..0b5ed2b
--- /dev/null
+++ b/vm/app/catgirl/shell.nix
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: EUPL-1.2
+# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+
+{ pkgs ? import <nixpkgs> {} }:
+
+with pkgs;
+
+(import ./. { inherit pkgs; }).overrideAttrs (
+{ passthru ? {}, nativeBuildInputs ? [], ... }:
+
+{
+  nativeBuildInputs = nativeBuildInputs ++ [
+    cloud-hypervisor jq qemu_kvm reuse
+  ];
+
+  KERNEL = "${passthru.kernel.dev}/vmlinux";
+})